blob: dc0e9c86065d3631f864371a08927d17ca377ac3 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 } else if (Array::IndexFromObject(*key, &element_index)) {
295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
572// Returns an array with the property description:
573// if args[1] is not a property on args[0]
574// returns undefined
575// if args[1] is a data property on args[0]
576// [false, value, Writeable, Enumerable, Configurable]
577// if args[1] is an accessor on args[0]
578// [true, GetFunction, SetFunction, Enumerable, Configurable]
579static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000580 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000581 HandleScope scope;
582 Handle<FixedArray> elms = Factory::NewFixedArray(5);
583 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
584 LookupResult result;
585 CONVERT_CHECKED(JSObject, obj, args[0]);
586 CONVERT_CHECKED(String, name, args[1]);
587
588 // Use recursive implementation to also traverse hidden prototypes
589 GetOwnPropertyImplementation(obj, name, &result);
590
591 if (!result.IsProperty())
592 return Heap::undefined_value();
593
594 if (result.type() == CALLBACKS) {
595 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000596 if (structure->IsProxy() || structure->IsAccessorInfo()) {
597 // Property that is internally implemented as a callback or
598 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000599 Object* value = obj->GetPropertyWithCallback(
600 obj, structure, name, result.holder());
601 elms->set(0, Heap::false_value());
602 elms->set(1, value);
603 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
604 } else if (structure->IsFixedArray()) {
605 // __defineGetter__/__defineSetter__ callback.
606 elms->set(0, Heap::true_value());
607 elms->set(1, FixedArray::cast(structure)->get(0));
608 elms->set(2, FixedArray::cast(structure)->get(1));
609 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000610 return Heap::undefined_value();
611 }
612 } else {
613 elms->set(0, Heap::false_value());
614 elms->set(1, result.GetLazyValue());
615 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
616 }
617
618 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000619 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000620 return *desc;
621}
622
623
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000624static Object* Runtime_IsExtensible(Arguments args) {
625 ASSERT(args.length() == 1);
626 CONVERT_CHECKED(JSObject, obj, args[0]);
627 return obj->map()->is_extensible() ? Heap::true_value()
628 : Heap::false_value();
629}
630
631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000632static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000633 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000635 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
636 CONVERT_ARG_CHECKED(String, pattern, 1);
637 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000638 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
639 if (result.is_null()) return Failure::Exception();
640 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641}
642
643
644static Object* Runtime_CreateApiFunction(Arguments args) {
645 HandleScope scope;
646 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000647 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000648 return *Factory::CreateApiFunction(data);
649}
650
651
652static Object* Runtime_IsTemplate(Arguments args) {
653 ASSERT(args.length() == 1);
654 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000655 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 return Heap::ToBoolean(result);
657}
658
659
660static Object* Runtime_GetTemplateField(Arguments args) {
661 ASSERT(args.length() == 2);
662 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000664 int index = field->value();
665 int offset = index * kPointerSize + HeapObject::kHeaderSize;
666 InstanceType type = templ->map()->instance_type();
667 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
668 type == OBJECT_TEMPLATE_INFO_TYPE);
669 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000670 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000671 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
672 } else {
673 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
674 }
675 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676}
677
678
ager@chromium.org870a0b62008-11-04 11:43:05 +0000679static Object* Runtime_DisableAccessChecks(Arguments args) {
680 ASSERT(args.length() == 1);
681 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000682 Map* old_map = object->map();
683 bool needs_access_checks = old_map->is_access_check_needed();
684 if (needs_access_checks) {
685 // Copy map so it won't interfere constructor's initial map.
686 Object* new_map = old_map->CopyDropTransitions();
687 if (new_map->IsFailure()) return new_map;
688
689 Map::cast(new_map)->set_is_access_check_needed(false);
690 object->set_map(Map::cast(new_map));
691 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000692 return needs_access_checks ? Heap::true_value() : Heap::false_value();
693}
694
695
696static Object* Runtime_EnableAccessChecks(Arguments args) {
697 ASSERT(args.length() == 1);
698 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000699 Map* old_map = object->map();
700 if (!old_map->is_access_check_needed()) {
701 // Copy map so it won't interfere constructor's initial map.
702 Object* new_map = old_map->CopyDropTransitions();
703 if (new_map->IsFailure()) return new_map;
704
705 Map::cast(new_map)->set_is_access_check_needed(true);
706 object->set_map(Map::cast(new_map));
707 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000708 return Heap::undefined_value();
709}
710
711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000712static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
713 HandleScope scope;
714 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
715 Handle<Object> args[2] = { type_handle, name };
716 Handle<Object> error =
717 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
718 return Top::Throw(*error);
719}
720
721
722static Object* Runtime_DeclareGlobals(Arguments args) {
723 HandleScope scope;
724 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
725
ager@chromium.org3811b432009-10-28 14:53:37 +0000726 Handle<Context> context = args.at<Context>(0);
727 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 bool is_eval = Smi::cast(args[2])->value() == 1;
729
730 // Compute the property attributes. According to ECMA-262, section
731 // 13, page 71, the property must be read-only and
732 // non-deletable. However, neither SpiderMonkey nor KJS creates the
733 // property as read-only, so we don't either.
734 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Traverse the name/value pairs and set the properties.
737 int length = pairs->length();
738 for (int i = 0; i < length; i += 2) {
739 HandleScope scope;
740 Handle<String> name(String::cast(pairs->get(i)));
741 Handle<Object> value(pairs->get(i + 1));
742
743 // We have to declare a global const property. To capture we only
744 // assign to it when evaluating the assignment for "const x =
745 // <expr>" the initial value is the hole.
746 bool is_const_property = value->IsTheHole();
747
748 if (value->IsUndefined() || is_const_property) {
749 // Lookup the property in the global object, and don't set the
750 // value of the variable if the property is already there.
751 LookupResult lookup;
752 global->Lookup(*name, &lookup);
753 if (lookup.IsProperty()) {
754 // Determine if the property is local by comparing the holder
755 // against the global object. The information will be used to
756 // avoid throwing re-declaration errors when declaring
757 // variables or constants that exist in the prototype chain.
758 bool is_local = (*global == lookup.holder());
759 // Get the property attributes and determine if the property is
760 // read-only.
761 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
762 bool is_read_only = (attributes & READ_ONLY) != 0;
763 if (lookup.type() == INTERCEPTOR) {
764 // If the interceptor says the property is there, we
765 // just return undefined without overwriting the property.
766 // Otherwise, we continue to setting the property.
767 if (attributes != ABSENT) {
768 // Check if the existing property conflicts with regards to const.
769 if (is_local && (is_read_only || is_const_property)) {
770 const char* type = (is_read_only) ? "const" : "var";
771 return ThrowRedeclarationError(type, name);
772 };
773 // The property already exists without conflicting: Go to
774 // the next declaration.
775 continue;
776 }
777 // Fall-through and introduce the absent property by using
778 // SetProperty.
779 } else {
780 if (is_local && (is_read_only || is_const_property)) {
781 const char* type = (is_read_only) ? "const" : "var";
782 return ThrowRedeclarationError(type, name);
783 }
784 // The property already exists without conflicting: Go to
785 // the next declaration.
786 continue;
787 }
788 }
789 } else {
790 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000791 Handle<SharedFunctionInfo> shared =
792 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000794 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795 value = function;
796 }
797
798 LookupResult lookup;
799 global->LocalLookup(*name, &lookup);
800
801 PropertyAttributes attributes = is_const_property
802 ? static_cast<PropertyAttributes>(base | READ_ONLY)
803 : base;
804
805 if (lookup.IsProperty()) {
806 // There's a local property that we need to overwrite because
807 // we're either declaring a function or there's an interceptor
808 // that claims the property is absent.
809
810 // Check for conflicting re-declarations. We cannot have
811 // conflicting types in case of intercepted properties because
812 // they are absent.
813 if (lookup.type() != INTERCEPTOR &&
814 (lookup.IsReadOnly() || is_const_property)) {
815 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
816 return ThrowRedeclarationError(type, name);
817 }
818 SetProperty(global, name, value, attributes);
819 } else {
820 // If a property with this name does not already exist on the
821 // global object add the property locally. We take special
822 // precautions to always add it as a local property even in case
823 // of callbacks in the prototype chain (this rules out using
824 // SetProperty). Also, we must use the handle-based version to
825 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000826 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 }
828 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 return Heap::undefined_value();
831}
832
833
834static Object* Runtime_DeclareContextSlot(Arguments args) {
835 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000836 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 CONVERT_ARG_CHECKED(Context, context, 0);
839 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000841 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000843 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000844
845 // Declarations are always done in the function context.
846 context = Handle<Context>(context->fcontext());
847
848 int index;
849 PropertyAttributes attributes;
850 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000851 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 context->Lookup(name, flags, &index, &attributes);
853
854 if (attributes != ABSENT) {
855 // The name was declared before; check for conflicting
856 // re-declarations: This is similar to the code in parser.cc in
857 // the AstBuildingParser::Declare function.
858 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
859 // Functions are not read-only.
860 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
861 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
862 return ThrowRedeclarationError(type, name);
863 }
864
865 // Initialize it if necessary.
866 if (*initial_value != NULL) {
867 if (index >= 0) {
868 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000869 // the function context or the arguments object.
870 if (holder->IsContext()) {
871 ASSERT(holder.is_identical_to(context));
872 if (((attributes & READ_ONLY) == 0) ||
873 context->get(index)->IsTheHole()) {
874 context->set(index, *initial_value);
875 }
876 } else {
877 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 }
879 } else {
880 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000881 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 SetProperty(context_ext, name, initial_value, mode);
883 }
884 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000886 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000887 // The property is not in the function context. It needs to be
888 // "declared" in the function context's extension context, or in the
889 // global context.
890 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000891 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892 // The function context's extension context exists - use it.
893 context_ext = Handle<JSObject>(context->extension());
894 } else {
895 // The function context's extension context does not exists - allocate
896 // it.
897 context_ext = Factory::NewJSObject(Top::context_extension_function());
898 // And store it in the extension slot.
899 context->set_extension(*context_ext);
900 }
901 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902
ager@chromium.org7c537e22008-10-16 08:43:32 +0000903 // Declare the property by setting it to the initial value if provided,
904 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
905 // constant declarations).
906 ASSERT(!context_ext->HasLocalProperty(*name));
907 Handle<Object> value(Heap::undefined_value());
908 if (*initial_value != NULL) value = initial_value;
909 SetProperty(context_ext, name, value, mode);
910 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
911 }
912
913 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914}
915
916
917static Object* Runtime_InitializeVarGlobal(Arguments args) {
918 NoHandleAllocation nha;
919
920 // Determine if we need to assign to the variable if it already
921 // exists (based on the number of arguments).
922 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
923 bool assign = args.length() == 2;
924
925 CONVERT_ARG_CHECKED(String, name, 0);
926 GlobalObject* global = Top::context()->global();
927
928 // According to ECMA-262, section 12.2, page 62, the property must
929 // not be deletable.
930 PropertyAttributes attributes = DONT_DELETE;
931
932 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000933 // there, there is a property with this name in the prototype chain.
934 // We follow Safari and Firefox behavior and only set the property
935 // locally if there is an explicit initialization value that we have
936 // to assign to the property. When adding the property we take
937 // special precautions to always add it as a local property even in
938 // case of callbacks in the prototype chain (this rules out using
939 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
940 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000941 // Note that objects can have hidden prototypes, so we need to traverse
942 // the whole chain of hidden prototypes to do a 'local' lookup.
943 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000945 while (true) {
946 real_holder->LocalLookup(*name, &lookup);
947 if (lookup.IsProperty()) {
948 // Determine if this is a redeclaration of something read-only.
949 if (lookup.IsReadOnly()) {
950 // If we found readonly property on one of hidden prototypes,
951 // just shadow it.
952 if (real_holder != Top::context()->global()) break;
953 return ThrowRedeclarationError("const", name);
954 }
955
956 // Determine if this is a redeclaration of an intercepted read-only
957 // property and figure out if the property exists at all.
958 bool found = true;
959 PropertyType type = lookup.type();
960 if (type == INTERCEPTOR) {
961 HandleScope handle_scope;
962 Handle<JSObject> holder(real_holder);
963 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
964 real_holder = *holder;
965 if (intercepted == ABSENT) {
966 // The interceptor claims the property isn't there. We need to
967 // make sure to introduce it.
968 found = false;
969 } else if ((intercepted & READ_ONLY) != 0) {
970 // The property is present, but read-only. Since we're trying to
971 // overwrite it with a variable declaration we must throw a
972 // re-declaration error. However if we found readonly property
973 // on one of hidden prototypes, just shadow it.
974 if (real_holder != Top::context()->global()) break;
975 return ThrowRedeclarationError("const", name);
976 }
977 }
978
979 if (found && !assign) {
980 // The global property is there and we're not assigning any value
981 // to it. Just return.
982 return Heap::undefined_value();
983 }
984
985 // Assign the value (or undefined) to the property.
986 Object* value = (assign) ? args[1] : Heap::undefined_value();
987 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000988 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000989
990 Object* proto = real_holder->GetPrototype();
991 if (!proto->IsJSObject())
992 break;
993
994 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
995 break;
996
997 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998 }
999
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001000 global = Top::context()->global();
1001 if (assign) {
1002 return global->IgnoreAttributesAndSetLocalProperty(*name,
1003 args[1],
1004 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007}
1008
1009
1010static Object* Runtime_InitializeConstGlobal(Arguments args) {
1011 // All constants are declared with an initial value. The name
1012 // of the constant is the first argument and the initial value
1013 // is the second.
1014 RUNTIME_ASSERT(args.length() == 2);
1015 CONVERT_ARG_CHECKED(String, name, 0);
1016 Handle<Object> value = args.at<Object>(1);
1017
1018 // Get the current global object from top.
1019 GlobalObject* global = Top::context()->global();
1020
1021 // According to ECMA-262, section 12.2, page 62, the property must
1022 // not be deletable. Since it's a const, it must be READ_ONLY too.
1023 PropertyAttributes attributes =
1024 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1025
1026 // Lookup the property locally in the global object. If it isn't
1027 // there, we add the property and take special precautions to always
1028 // add it as a local property even in case of callbacks in the
1029 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031 LookupResult lookup;
1032 global->LocalLookup(*name, &lookup);
1033 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001034 return global->IgnoreAttributesAndSetLocalProperty(*name,
1035 *value,
1036 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 }
1038
1039 // Determine if this is a redeclaration of something not
1040 // read-only. In case the result is hidden behind an interceptor we
1041 // need to ask it for the property attributes.
1042 if (!lookup.IsReadOnly()) {
1043 if (lookup.type() != INTERCEPTOR) {
1044 return ThrowRedeclarationError("var", name);
1045 }
1046
1047 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1048
1049 // Throw re-declaration error if the intercepted property is present
1050 // but not read-only.
1051 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1052 return ThrowRedeclarationError("var", name);
1053 }
1054
1055 // Restore global object from context (in case of GC) and continue
1056 // with setting the value because the property is either absent or
1057 // read-only. We also have to do redo the lookup.
1058 global = Top::context()->global();
1059
1060 // BUG 1213579: Handle the case where we have to set a read-only
1061 // property through an interceptor and only do it if it's
1062 // uninitialized, e.g. the hole. Nirk...
1063 global->SetProperty(*name, *value, attributes);
1064 return *value;
1065 }
1066
1067 // Set the value, but only we're assigning the initial value to a
1068 // constant. For now, we determine this by checking if the
1069 // current value is the hole.
1070 PropertyType type = lookup.type();
1071 if (type == FIELD) {
1072 FixedArray* properties = global->properties();
1073 int index = lookup.GetFieldIndex();
1074 if (properties->get(index)->IsTheHole()) {
1075 properties->set(index, *value);
1076 }
1077 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001078 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1079 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
1081 } else {
1082 // Ignore re-initialization of constants that have already been
1083 // assigned a function value.
1084 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1085 }
1086
1087 // Use the set value as the result of the operation.
1088 return *value;
1089}
1090
1091
1092static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1093 HandleScope scope;
1094 ASSERT(args.length() == 3);
1095
1096 Handle<Object> value(args[0]);
1097 ASSERT(!value->IsTheHole());
1098 CONVERT_ARG_CHECKED(Context, context, 1);
1099 Handle<String> name(String::cast(args[2]));
1100
1101 // Initializations are always done in the function context.
1102 context = Handle<Context>(context->fcontext());
1103
1104 int index;
1105 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001106 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001107 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001108 context->Lookup(name, flags, &index, &attributes);
1109
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001110 // In most situations, the property introduced by the const
1111 // declaration should be present in the context extension object.
1112 // However, because declaration and initialization are separate, the
1113 // property might have been deleted (if it was introduced by eval)
1114 // before we reach the initialization point.
1115 //
1116 // Example:
1117 //
1118 // function f() { eval("delete x; const x;"); }
1119 //
1120 // In that case, the initialization behaves like a normal assignment
1121 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001123 // Property was found in a context.
1124 if (holder->IsContext()) {
1125 // The holder cannot be the function context. If it is, there
1126 // should have been a const redeclaration error when declaring
1127 // the const property.
1128 ASSERT(!holder.is_identical_to(context));
1129 if ((attributes & READ_ONLY) == 0) {
1130 Handle<Context>::cast(holder)->set(index, *value);
1131 }
1132 } else {
1133 // The holder is an arguments object.
1134 ASSERT((attributes & READ_ONLY) == 0);
1135 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137 return *value;
1138 }
1139
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001140 // The property could not be found, we introduce it in the global
1141 // context.
1142 if (attributes == ABSENT) {
1143 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1144 SetProperty(global, name, value, NONE);
1145 return *value;
1146 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001147
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001148 // The property was present in a context extension object.
1149 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001151 if (*context_ext == context->extension()) {
1152 // This is the property that was introduced by the const
1153 // declaration. Set it if it hasn't been set before. NOTE: We
1154 // cannot use GetProperty() to get the current value as it
1155 // 'unholes' the value.
1156 LookupResult lookup;
1157 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1158 ASSERT(lookup.IsProperty()); // the property was declared
1159 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1160
1161 PropertyType type = lookup.type();
1162 if (type == FIELD) {
1163 FixedArray* properties = context_ext->properties();
1164 int index = lookup.GetFieldIndex();
1165 if (properties->get(index)->IsTheHole()) {
1166 properties->set(index, *value);
1167 }
1168 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001169 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1170 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001171 }
1172 } else {
1173 // We should not reach here. Any real, named property should be
1174 // either a field or a dictionary slot.
1175 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176 }
1177 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001178 // The property was found in a different context extension object.
1179 // Set it if it is not a read-only property.
1180 if ((attributes & READ_ONLY) == 0) {
1181 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1182 // Setting a property might throw an exception. Exceptions
1183 // are converted to empty handles in handle operations. We
1184 // need to convert back to exceptions here.
1185 if (set.is_null()) {
1186 ASSERT(Top::has_pending_exception());
1187 return Failure::Exception();
1188 }
1189 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 return *value;
1193}
1194
1195
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001196static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1197 Arguments args) {
1198 HandleScope scope;
1199 ASSERT(args.length() == 2);
1200 CONVERT_ARG_CHECKED(JSObject, object, 0);
1201 CONVERT_SMI_CHECKED(properties, args[1]);
1202 if (object->HasFastProperties()) {
1203 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1204 }
1205 return *object;
1206}
1207
1208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209static Object* Runtime_RegExpExec(Arguments args) {
1210 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001211 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001212 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1213 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001214 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001215 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001216 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001217 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001218 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001219 RUNTIME_ASSERT(index >= 0);
1220 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001221 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001222 Handle<Object> result = RegExpImpl::Exec(regexp,
1223 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001224 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001225 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001226 if (result.is_null()) return Failure::Exception();
1227 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228}
1229
1230
lrn@chromium.org25156de2010-04-06 13:10:27 +00001231static Object* Runtime_RegExpInitializeObject(Arguments args) {
1232 AssertNoAllocation no_alloc;
1233 ASSERT(args.length() == 5);
1234 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1235 CONVERT_CHECKED(String, source, args[1]);
1236
1237 Object* global = args[2];
1238 if (!global->IsTrue()) global = Heap::false_value();
1239
1240 Object* ignoreCase = args[3];
1241 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1242
1243 Object* multiline = args[4];
1244 if (!multiline->IsTrue()) multiline = Heap::false_value();
1245
1246 Map* map = regexp->map();
1247 Object* constructor = map->constructor();
1248 if (constructor->IsJSFunction() &&
1249 JSFunction::cast(constructor)->initial_map() == map) {
1250 // If we still have the original map, set in-object properties directly.
1251 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1252 // TODO(lrn): Consider skipping write barrier on booleans as well.
1253 // Both true and false should be in oldspace at all times.
1254 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1255 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1256 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1257 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1258 Smi::FromInt(0),
1259 SKIP_WRITE_BARRIER);
1260 return regexp;
1261 }
1262
1263 // Map has changed, so use generic, but slower, method.
1264 PropertyAttributes final =
1265 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1266 PropertyAttributes writable =
1267 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1268 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1269 source,
1270 final);
1271 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1272 global,
1273 final);
1274 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1275 ignoreCase,
1276 final);
1277 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1278 multiline,
1279 final);
1280 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1281 Smi::FromInt(0),
1282 writable);
1283 return regexp;
1284}
1285
1286
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001287static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1288 HandleScope scope;
1289 ASSERT(args.length() == 1);
1290 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1291 // This is necessary to enable fast checks for absence of elements
1292 // on Array.prototype and below.
1293 prototype->set_elements(Heap::empty_fixed_array());
1294 return Smi::FromInt(0);
1295}
1296
1297
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001298static void SetCustomCallGenerator(Handle<JSFunction> function,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001299 ExternalReference* generator) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001300 if (function->shared()->function_data()->IsUndefined()) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001301 function->shared()->set_function_data(*FromCData(generator->address()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001302 }
1303}
1304
1305
1306static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1307 const char* name,
1308 Builtins::Name builtin_name,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001309 ExternalReference* generator = NULL) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001310 Handle<String> key = Factory::LookupAsciiSymbol(name);
1311 Handle<Code> code(Builtins::builtin(builtin_name));
1312 Handle<JSFunction> optimized = Factory::NewFunction(key,
1313 JS_OBJECT_TYPE,
1314 JSObject::kHeaderSize,
1315 code,
1316 false);
1317 optimized->shared()->DontAdaptArguments();
1318 if (generator != NULL) {
1319 SetCustomCallGenerator(optimized, generator);
1320 }
1321 SetProperty(holder, key, optimized, NONE);
1322 return optimized;
1323}
1324
1325
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001326Object* CompileArrayPushCall(CallStubCompiler* compiler,
1327 Object* object,
1328 JSObject* holder,
1329 JSFunction* function,
1330 String* name,
1331 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001332 return compiler->CompileArrayPushCall(object, holder, function, name, check);
1333}
1334
1335
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001336Object* CompileArrayPopCall(CallStubCompiler* compiler,
1337 Object* object,
1338 JSObject* holder,
1339 JSFunction* function,
1340 String* name,
1341 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001342 return compiler->CompileArrayPopCall(object, holder, function, name, check);
1343}
1344
1345
1346static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1347 HandleScope scope;
1348 ASSERT(args.length() == 1);
1349 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1350
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001351 ExternalReference pop = ExternalReference::compile_array_pop_call();
1352 ExternalReference push = ExternalReference::compile_array_push_call();
1353
1354 InstallBuiltin(holder, "pop", Builtins::ArrayPop, &pop);
1355 InstallBuiltin(holder, "push", Builtins::ArrayPush, &push);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001356 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1357 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1358 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1359 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001360 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001361
1362 return *holder;
1363}
1364
1365
ager@chromium.org357bf652010-04-12 11:30:10 +00001366static Object* Runtime_GetGlobalReceiver(Arguments args) {
1367 // Returns a real global receiver, not one of builtins object.
1368 Context* global_context = Top::context()->global()->global_context();
1369 return global_context->global()->global_receiver();
1370}
1371
1372
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001373static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1374 HandleScope scope;
1375 ASSERT(args.length() == 4);
1376 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1377 int index = Smi::cast(args[1])->value();
1378 Handle<String> pattern = args.at<String>(2);
1379 Handle<String> flags = args.at<String>(3);
1380
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001381 // Get the RegExp function from the context in the literals array.
1382 // This is the RegExp function from the context in which the
1383 // function was created. We do not use the RegExp function from the
1384 // current global context because this might be the RegExp function
1385 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001386 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001387 Handle<JSFunction>(
1388 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001389 // Compute the regular expression literal.
1390 bool has_pending_exception;
1391 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001392 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1393 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001394 if (has_pending_exception) {
1395 ASSERT(Top::has_pending_exception());
1396 return Failure::Exception();
1397 }
1398 literals->set(index, *regexp);
1399 return *regexp;
1400}
1401
1402
1403static Object* Runtime_FunctionGetName(Arguments args) {
1404 NoHandleAllocation ha;
1405 ASSERT(args.length() == 1);
1406
1407 CONVERT_CHECKED(JSFunction, f, args[0]);
1408 return f->shared()->name();
1409}
1410
1411
ager@chromium.org236ad962008-09-25 09:45:57 +00001412static Object* Runtime_FunctionSetName(Arguments args) {
1413 NoHandleAllocation ha;
1414 ASSERT(args.length() == 2);
1415
1416 CONVERT_CHECKED(JSFunction, f, args[0]);
1417 CONVERT_CHECKED(String, name, args[1]);
1418 f->shared()->set_name(name);
1419 return Heap::undefined_value();
1420}
1421
1422
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001423static Object* Runtime_FunctionGetScript(Arguments args) {
1424 HandleScope scope;
1425 ASSERT(args.length() == 1);
1426
1427 CONVERT_CHECKED(JSFunction, fun, args[0]);
1428 Handle<Object> script = Handle<Object>(fun->shared()->script());
1429 if (!script->IsScript()) return Heap::undefined_value();
1430
1431 return *GetScriptWrapper(Handle<Script>::cast(script));
1432}
1433
1434
1435static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1436 NoHandleAllocation ha;
1437 ASSERT(args.length() == 1);
1438
1439 CONVERT_CHECKED(JSFunction, f, args[0]);
1440 return f->shared()->GetSourceCode();
1441}
1442
1443
1444static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1445 NoHandleAllocation ha;
1446 ASSERT(args.length() == 1);
1447
1448 CONVERT_CHECKED(JSFunction, fun, args[0]);
1449 int pos = fun->shared()->start_position();
1450 return Smi::FromInt(pos);
1451}
1452
1453
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001454static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1455 ASSERT(args.length() == 2);
1456
1457 CONVERT_CHECKED(JSFunction, fun, args[0]);
1458 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1459
1460 Code* code = fun->code();
1461 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1462
1463 Address pc = code->address() + offset;
1464 return Smi::FromInt(fun->code()->SourcePosition(pc));
1465}
1466
1467
1468
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001469static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1470 NoHandleAllocation ha;
1471 ASSERT(args.length() == 2);
1472
1473 CONVERT_CHECKED(JSFunction, fun, args[0]);
1474 CONVERT_CHECKED(String, name, args[1]);
1475 fun->SetInstanceClassName(name);
1476 return Heap::undefined_value();
1477}
1478
1479
1480static Object* Runtime_FunctionSetLength(Arguments args) {
1481 NoHandleAllocation ha;
1482 ASSERT(args.length() == 2);
1483
1484 CONVERT_CHECKED(JSFunction, fun, args[0]);
1485 CONVERT_CHECKED(Smi, length, args[1]);
1486 fun->shared()->set_length(length->value());
1487 return length;
1488}
1489
1490
1491static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001492 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001493 ASSERT(args.length() == 2);
1494
1495 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001496 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1497 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001498 return args[0]; // return TOS
1499}
1500
1501
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001502static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1503 NoHandleAllocation ha;
1504 ASSERT(args.length() == 1);
1505
1506 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001507 return f->shared()->IsApiFunction() ? Heap::true_value()
1508 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001509}
1510
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001511static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1512 NoHandleAllocation ha;
1513 ASSERT(args.length() == 1);
1514
1515 CONVERT_CHECKED(JSFunction, f, args[0]);
1516 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1517}
1518
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001520static Object* Runtime_SetCode(Arguments args) {
1521 HandleScope scope;
1522 ASSERT(args.length() == 2);
1523
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001524 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001525 Handle<Object> code = args.at<Object>(1);
1526
1527 Handle<Context> context(target->context());
1528
1529 if (!code->IsNull()) {
1530 RUNTIME_ASSERT(code->IsJSFunction());
1531 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001532 Handle<SharedFunctionInfo> shared(fun->shared());
1533 SetExpectedNofProperties(target, shared->expected_nof_properties());
1534
1535 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001536 return Failure::Exception();
1537 }
1538 // Set the code, formal parameter count, and the length of the target
1539 // function.
1540 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001541 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001542 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001543 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001544 // Set the source code of the target function to undefined.
1545 // SetCode is only used for built-in constructors like String,
1546 // Array, and Object, and some web code
1547 // doesn't like seeing source code for constructors.
1548 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001549 // Clear the optimization hints related to the compiled code as these are no
1550 // longer valid when the code is overwritten.
1551 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001552 context = Handle<Context>(fun->context());
1553
1554 // Make sure we get a fresh copy of the literal vector to avoid
1555 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001556 int number_of_literals = fun->NumberOfLiterals();
1557 Handle<FixedArray> literals =
1558 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001559 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001560 // Insert the object, regexp and array functions in the literals
1561 // array prefix. These are the functions that will be used when
1562 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001563 literals->set(JSFunction::kLiteralGlobalContextIndex,
1564 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001565 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001566 // It's okay to skip the write barrier here because the literals
1567 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001568 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001569 }
1570
1571 target->set_context(*context);
1572 return *target;
1573}
1574
1575
1576static Object* CharCodeAt(String* subject, Object* index) {
1577 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001578 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001579 // Flatten the string. If someone wants to get a char at an index
1580 // in a cons string, it is likely that more indices will be
1581 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001582 Object* flat = subject->TryFlatten();
1583 if (flat->IsFailure()) return flat;
1584 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001585 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001586 return Heap::nan_value();
1587 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001588 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001589}
1590
1591
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001592static Object* CharFromCode(Object* char_code) {
1593 uint32_t code;
1594 if (Array::IndexFromObject(char_code, &code)) {
1595 if (code <= 0xffff) {
1596 return Heap::LookupSingleCharacterStringFromCode(code);
1597 }
1598 }
1599 return Heap::empty_string();
1600}
1601
1602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001603static Object* Runtime_StringCharCodeAt(Arguments args) {
1604 NoHandleAllocation ha;
1605 ASSERT(args.length() == 2);
1606
1607 CONVERT_CHECKED(String, subject, args[0]);
1608 Object* index = args[1];
1609 return CharCodeAt(subject, index);
1610}
1611
1612
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001613static Object* Runtime_StringCharAt(Arguments args) {
1614 NoHandleAllocation ha;
1615 ASSERT(args.length() == 2);
1616
1617 CONVERT_CHECKED(String, subject, args[0]);
1618 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001619 Object* code = CharCodeAt(subject, index);
1620 if (code == Heap::nan_value()) {
1621 return Heap::undefined_value();
1622 }
1623 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001624}
1625
1626
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001627static Object* Runtime_CharFromCode(Arguments args) {
1628 NoHandleAllocation ha;
1629 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001630 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001631}
1632
lrn@chromium.org25156de2010-04-06 13:10:27 +00001633
1634class FixedArrayBuilder {
1635 public:
1636 explicit FixedArrayBuilder(int initial_capacity)
1637 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1638 length_(0) {
1639 // Require a non-zero initial size. Ensures that doubling the size to
1640 // extend the array will work.
1641 ASSERT(initial_capacity > 0);
1642 }
1643
1644 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1645 : array_(backing_store),
1646 length_(0) {
1647 // Require a non-zero initial size. Ensures that doubling the size to
1648 // extend the array will work.
1649 ASSERT(backing_store->length() > 0);
1650 }
1651
1652 bool HasCapacity(int elements) {
1653 int length = array_->length();
1654 int required_length = length_ + elements;
1655 return (length >= required_length);
1656 }
1657
1658 void EnsureCapacity(int elements) {
1659 int length = array_->length();
1660 int required_length = length_ + elements;
1661 if (length < required_length) {
1662 int new_length = length;
1663 do {
1664 new_length *= 2;
1665 } while (new_length < required_length);
1666 Handle<FixedArray> extended_array =
1667 Factory::NewFixedArrayWithHoles(new_length);
1668 array_->CopyTo(0, *extended_array, 0, length_);
1669 array_ = extended_array;
1670 }
1671 }
1672
1673 void Add(Object* value) {
1674 ASSERT(length_ < capacity());
1675 array_->set(length_, value);
1676 length_++;
1677 }
1678
1679 void Add(Smi* value) {
1680 ASSERT(length_ < capacity());
1681 array_->set(length_, value);
1682 length_++;
1683 }
1684
1685 Handle<FixedArray> array() {
1686 return array_;
1687 }
1688
1689 int length() {
1690 return length_;
1691 }
1692
1693 int capacity() {
1694 return array_->length();
1695 }
1696
1697 Handle<JSArray> ToJSArray() {
1698 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1699 result_array->set_length(Smi::FromInt(length_));
1700 return result_array;
1701 }
1702
1703 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1704 target_array->set_elements(*array_);
1705 target_array->set_length(Smi::FromInt(length_));
1706 return target_array;
1707 }
1708
1709 private:
1710 Handle<FixedArray> array_;
1711 int length_;
1712};
1713
1714
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001715// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001716const int kStringBuilderConcatHelperLengthBits = 11;
1717const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001718
1719template <typename schar>
1720static inline void StringBuilderConcatHelper(String*,
1721 schar*,
1722 FixedArray*,
1723 int);
1724
lrn@chromium.org25156de2010-04-06 13:10:27 +00001725typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1726 StringBuilderSubstringLength;
1727typedef BitField<int,
1728 kStringBuilderConcatHelperLengthBits,
1729 kStringBuilderConcatHelperPositionBits>
1730 StringBuilderSubstringPosition;
1731
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001732
1733class ReplacementStringBuilder {
1734 public:
1735 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001736 : array_builder_(estimated_part_count),
1737 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001738 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001739 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001740 // Require a non-zero initial size. Ensures that doubling the size to
1741 // extend the array will work.
1742 ASSERT(estimated_part_count > 0);
1743 }
1744
lrn@chromium.org25156de2010-04-06 13:10:27 +00001745 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1746 int from,
1747 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001748 ASSERT(from >= 0);
1749 int length = to - from;
1750 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001751 if (StringBuilderSubstringLength::is_valid(length) &&
1752 StringBuilderSubstringPosition::is_valid(from)) {
1753 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1754 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001755 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001756 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001757 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001758 builder->Add(Smi::FromInt(-length));
1759 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001760 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001761 }
1762
1763
1764 void EnsureCapacity(int elements) {
1765 array_builder_.EnsureCapacity(elements);
1766 }
1767
1768
1769 void AddSubjectSlice(int from, int to) {
1770 AddSubjectSlice(&array_builder_, from, to);
1771 // Can we encode the slice in 11 bits for length and 19 bits for
1772 // start position - as used by StringBuilderConcatHelper?
1773 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001774 }
1775
1776
1777 void AddString(Handle<String> string) {
1778 int length = string->length();
1779 ASSERT(length > 0);
1780 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001781 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001782 is_ascii_ = false;
1783 }
1784 IncrementCharacterCount(length);
1785 }
1786
1787
1788 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001789 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001790 return Factory::empty_string();
1791 }
1792
1793 Handle<String> joined_string;
1794 if (is_ascii_) {
1795 joined_string = NewRawAsciiString(character_count_);
1796 AssertNoAllocation no_alloc;
1797 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1798 char* char_buffer = seq->GetChars();
1799 StringBuilderConcatHelper(*subject_,
1800 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801 *array_builder_.array(),
1802 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001803 } else {
1804 // Non-ASCII.
1805 joined_string = NewRawTwoByteString(character_count_);
1806 AssertNoAllocation no_alloc;
1807 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1808 uc16* char_buffer = seq->GetChars();
1809 StringBuilderConcatHelper(*subject_,
1810 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001811 *array_builder_.array(),
1812 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001813 }
1814 return joined_string;
1815 }
1816
1817
1818 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001819 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001820 V8::FatalProcessOutOfMemory("String.replace result too large.");
1821 }
1822 character_count_ += by;
1823 }
1824
lrn@chromium.org25156de2010-04-06 13:10:27 +00001825 Handle<JSArray> GetParts() {
1826 Handle<JSArray> result =
1827 Factory::NewJSArrayWithElements(array_builder_.array());
1828 result->set_length(Smi::FromInt(array_builder_.length()));
1829 return result;
1830 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001831
lrn@chromium.org25156de2010-04-06 13:10:27 +00001832 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001833 Handle<String> NewRawAsciiString(int size) {
1834 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1835 }
1836
1837
1838 Handle<String> NewRawTwoByteString(int size) {
1839 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1840 }
1841
1842
1843 void AddElement(Object* element) {
1844 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001845 ASSERT(array_builder_.capacity() > array_builder_.length());
1846 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001847 }
1848
lrn@chromium.org25156de2010-04-06 13:10:27 +00001849 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001850 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001851 int character_count_;
1852 bool is_ascii_;
1853};
1854
1855
1856class CompiledReplacement {
1857 public:
1858 CompiledReplacement()
1859 : parts_(1), replacement_substrings_(0) {}
1860
1861 void Compile(Handle<String> replacement,
1862 int capture_count,
1863 int subject_length);
1864
1865 void Apply(ReplacementStringBuilder* builder,
1866 int match_from,
1867 int match_to,
1868 Handle<JSArray> last_match_info);
1869
1870 // Number of distinct parts of the replacement pattern.
1871 int parts() {
1872 return parts_.length();
1873 }
1874 private:
1875 enum PartType {
1876 SUBJECT_PREFIX = 1,
1877 SUBJECT_SUFFIX,
1878 SUBJECT_CAPTURE,
1879 REPLACEMENT_SUBSTRING,
1880 REPLACEMENT_STRING,
1881
1882 NUMBER_OF_PART_TYPES
1883 };
1884
1885 struct ReplacementPart {
1886 static inline ReplacementPart SubjectMatch() {
1887 return ReplacementPart(SUBJECT_CAPTURE, 0);
1888 }
1889 static inline ReplacementPart SubjectCapture(int capture_index) {
1890 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1891 }
1892 static inline ReplacementPart SubjectPrefix() {
1893 return ReplacementPart(SUBJECT_PREFIX, 0);
1894 }
1895 static inline ReplacementPart SubjectSuffix(int subject_length) {
1896 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1897 }
1898 static inline ReplacementPart ReplacementString() {
1899 return ReplacementPart(REPLACEMENT_STRING, 0);
1900 }
1901 static inline ReplacementPart ReplacementSubString(int from, int to) {
1902 ASSERT(from >= 0);
1903 ASSERT(to > from);
1904 return ReplacementPart(-from, to);
1905 }
1906
1907 // If tag <= 0 then it is the negation of a start index of a substring of
1908 // the replacement pattern, otherwise it's a value from PartType.
1909 ReplacementPart(int tag, int data)
1910 : tag(tag), data(data) {
1911 // Must be non-positive or a PartType value.
1912 ASSERT(tag < NUMBER_OF_PART_TYPES);
1913 }
1914 // Either a value of PartType or a non-positive number that is
1915 // the negation of an index into the replacement string.
1916 int tag;
1917 // The data value's interpretation depends on the value of tag:
1918 // tag == SUBJECT_PREFIX ||
1919 // tag == SUBJECT_SUFFIX: data is unused.
1920 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1921 // tag == REPLACEMENT_SUBSTRING ||
1922 // tag == REPLACEMENT_STRING: data is index into array of substrings
1923 // of the replacement string.
1924 // tag <= 0: Temporary representation of the substring of the replacement
1925 // string ranging over -tag .. data.
1926 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1927 // substring objects.
1928 int data;
1929 };
1930
1931 template<typename Char>
1932 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1933 Vector<Char> characters,
1934 int capture_count,
1935 int subject_length) {
1936 int length = characters.length();
1937 int last = 0;
1938 for (int i = 0; i < length; i++) {
1939 Char c = characters[i];
1940 if (c == '$') {
1941 int next_index = i + 1;
1942 if (next_index == length) { // No next character!
1943 break;
1944 }
1945 Char c2 = characters[next_index];
1946 switch (c2) {
1947 case '$':
1948 if (i > last) {
1949 // There is a substring before. Include the first "$".
1950 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1951 last = next_index + 1; // Continue after the second "$".
1952 } else {
1953 // Let the next substring start with the second "$".
1954 last = next_index;
1955 }
1956 i = next_index;
1957 break;
1958 case '`':
1959 if (i > last) {
1960 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1961 }
1962 parts->Add(ReplacementPart::SubjectPrefix());
1963 i = next_index;
1964 last = i + 1;
1965 break;
1966 case '\'':
1967 if (i > last) {
1968 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1969 }
1970 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1971 i = next_index;
1972 last = i + 1;
1973 break;
1974 case '&':
1975 if (i > last) {
1976 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1977 }
1978 parts->Add(ReplacementPart::SubjectMatch());
1979 i = next_index;
1980 last = i + 1;
1981 break;
1982 case '0':
1983 case '1':
1984 case '2':
1985 case '3':
1986 case '4':
1987 case '5':
1988 case '6':
1989 case '7':
1990 case '8':
1991 case '9': {
1992 int capture_ref = c2 - '0';
1993 if (capture_ref > capture_count) {
1994 i = next_index;
1995 continue;
1996 }
1997 int second_digit_index = next_index + 1;
1998 if (second_digit_index < length) {
1999 // Peek ahead to see if we have two digits.
2000 Char c3 = characters[second_digit_index];
2001 if ('0' <= c3 && c3 <= '9') { // Double digits.
2002 int double_digit_ref = capture_ref * 10 + c3 - '0';
2003 if (double_digit_ref <= capture_count) {
2004 next_index = second_digit_index;
2005 capture_ref = double_digit_ref;
2006 }
2007 }
2008 }
2009 if (capture_ref > 0) {
2010 if (i > last) {
2011 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2012 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002013 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002014 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2015 last = next_index + 1;
2016 }
2017 i = next_index;
2018 break;
2019 }
2020 default:
2021 i = next_index;
2022 break;
2023 }
2024 }
2025 }
2026 if (length > last) {
2027 if (last == 0) {
2028 parts->Add(ReplacementPart::ReplacementString());
2029 } else {
2030 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2031 }
2032 }
2033 }
2034
2035 ZoneList<ReplacementPart> parts_;
2036 ZoneList<Handle<String> > replacement_substrings_;
2037};
2038
2039
2040void CompiledReplacement::Compile(Handle<String> replacement,
2041 int capture_count,
2042 int subject_length) {
2043 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002044 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002045 AssertNoAllocation no_alloc;
2046 ParseReplacementPattern(&parts_,
2047 replacement->ToAsciiVector(),
2048 capture_count,
2049 subject_length);
2050 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002051 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002052 AssertNoAllocation no_alloc;
2053
2054 ParseReplacementPattern(&parts_,
2055 replacement->ToUC16Vector(),
2056 capture_count,
2057 subject_length);
2058 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002059 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002060 int substring_index = 0;
2061 for (int i = 0, n = parts_.length(); i < n; i++) {
2062 int tag = parts_[i].tag;
2063 if (tag <= 0) { // A replacement string slice.
2064 int from = -tag;
2065 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002066 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002067 parts_[i].tag = REPLACEMENT_SUBSTRING;
2068 parts_[i].data = substring_index;
2069 substring_index++;
2070 } else if (tag == REPLACEMENT_STRING) {
2071 replacement_substrings_.Add(replacement);
2072 parts_[i].data = substring_index;
2073 substring_index++;
2074 }
2075 }
2076}
2077
2078
2079void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2080 int match_from,
2081 int match_to,
2082 Handle<JSArray> last_match_info) {
2083 for (int i = 0, n = parts_.length(); i < n; i++) {
2084 ReplacementPart part = parts_[i];
2085 switch (part.tag) {
2086 case SUBJECT_PREFIX:
2087 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2088 break;
2089 case SUBJECT_SUFFIX: {
2090 int subject_length = part.data;
2091 if (match_to < subject_length) {
2092 builder->AddSubjectSlice(match_to, subject_length);
2093 }
2094 break;
2095 }
2096 case SUBJECT_CAPTURE: {
2097 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002098 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002099 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2100 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2101 if (from >= 0 && to > from) {
2102 builder->AddSubjectSlice(from, to);
2103 }
2104 break;
2105 }
2106 case REPLACEMENT_SUBSTRING:
2107 case REPLACEMENT_STRING:
2108 builder->AddString(replacement_substrings_[part.data]);
2109 break;
2110 default:
2111 UNREACHABLE();
2112 }
2113 }
2114}
2115
2116
2117
2118static Object* StringReplaceRegExpWithString(String* subject,
2119 JSRegExp* regexp,
2120 String* replacement,
2121 JSArray* last_match_info) {
2122 ASSERT(subject->IsFlat());
2123 ASSERT(replacement->IsFlat());
2124
2125 HandleScope handles;
2126
2127 int length = subject->length();
2128 Handle<String> subject_handle(subject);
2129 Handle<JSRegExp> regexp_handle(regexp);
2130 Handle<String> replacement_handle(replacement);
2131 Handle<JSArray> last_match_info_handle(last_match_info);
2132 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2133 subject_handle,
2134 0,
2135 last_match_info_handle);
2136 if (match.is_null()) {
2137 return Failure::Exception();
2138 }
2139 if (match->IsNull()) {
2140 return *subject_handle;
2141 }
2142
2143 int capture_count = regexp_handle->CaptureCount();
2144
2145 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002146 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002147 CompiledReplacement compiled_replacement;
2148 compiled_replacement.Compile(replacement_handle,
2149 capture_count,
2150 length);
2151
2152 bool is_global = regexp_handle->GetFlags().is_global();
2153
2154 // Guessing the number of parts that the final result string is built
2155 // from. Global regexps can match any number of times, so we guess
2156 // conservatively.
2157 int expected_parts =
2158 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2159 ReplacementStringBuilder builder(subject_handle, expected_parts);
2160
2161 // Index of end of last match.
2162 int prev = 0;
2163
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002164 // Number of parts added by compiled replacement plus preceeding
2165 // string and possibly suffix after last match. It is possible for
2166 // all components to use two elements when encoded as two smis.
2167 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002168 bool matched = true;
2169 do {
2170 ASSERT(last_match_info_handle->HasFastElements());
2171 // Increase the capacity of the builder before entering local handle-scope,
2172 // so its internal buffer can safely allocate a new handle if it grows.
2173 builder.EnsureCapacity(parts_added_per_loop);
2174
2175 HandleScope loop_scope;
2176 int start, end;
2177 {
2178 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002179 FixedArray* match_info_array =
2180 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002181
2182 ASSERT_EQ(capture_count * 2 + 2,
2183 RegExpImpl::GetLastCaptureCount(match_info_array));
2184 start = RegExpImpl::GetCapture(match_info_array, 0);
2185 end = RegExpImpl::GetCapture(match_info_array, 1);
2186 }
2187
2188 if (prev < start) {
2189 builder.AddSubjectSlice(prev, start);
2190 }
2191 compiled_replacement.Apply(&builder,
2192 start,
2193 end,
2194 last_match_info_handle);
2195 prev = end;
2196
2197 // Only continue checking for global regexps.
2198 if (!is_global) break;
2199
2200 // Continue from where the match ended, unless it was an empty match.
2201 int next = end;
2202 if (start == end) {
2203 next = end + 1;
2204 if (next > length) break;
2205 }
2206
2207 match = RegExpImpl::Exec(regexp_handle,
2208 subject_handle,
2209 next,
2210 last_match_info_handle);
2211 if (match.is_null()) {
2212 return Failure::Exception();
2213 }
2214 matched = !match->IsNull();
2215 } while (matched);
2216
2217 if (prev < length) {
2218 builder.AddSubjectSlice(prev, length);
2219 }
2220
2221 return *(builder.ToString());
2222}
2223
2224
2225static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2226 ASSERT(args.length() == 4);
2227
2228 CONVERT_CHECKED(String, subject, args[0]);
2229 if (!subject->IsFlat()) {
2230 Object* flat_subject = subject->TryFlatten();
2231 if (flat_subject->IsFailure()) {
2232 return flat_subject;
2233 }
2234 subject = String::cast(flat_subject);
2235 }
2236
2237 CONVERT_CHECKED(String, replacement, args[2]);
2238 if (!replacement->IsFlat()) {
2239 Object* flat_replacement = replacement->TryFlatten();
2240 if (flat_replacement->IsFailure()) {
2241 return flat_replacement;
2242 }
2243 replacement = String::cast(flat_replacement);
2244 }
2245
2246 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2247 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2248
2249 ASSERT(last_match_info->HasFastElements());
2250
2251 return StringReplaceRegExpWithString(subject,
2252 regexp,
2253 replacement,
2254 last_match_info);
2255}
2256
2257
ager@chromium.org7c537e22008-10-16 08:43:32 +00002258// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2259// limit, we can fix the size of tables.
2260static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002261// Reduce alphabet to this size.
2262static const int kBMAlphabetSize = 0x100;
2263// For patterns below this length, the skip length of Boyer-Moore is too short
2264// to compensate for the algorithmic overhead compared to simple brute force.
2265static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002266
ager@chromium.org7c537e22008-10-16 08:43:32 +00002267// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2268// shift. Only allows the last kBMMaxShift characters of the needle
2269// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002270class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002271 public:
2272 BMGoodSuffixBuffers() {}
2273 inline void init(int needle_length) {
2274 ASSERT(needle_length > 1);
2275 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2276 int len = needle_length - start;
2277 biased_suffixes_ = suffixes_ - start;
2278 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2279 for (int i = 0; i <= len; i++) {
2280 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002281 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002282 }
2283 inline int& suffix(int index) {
2284 ASSERT(biased_suffixes_ + index >= suffixes_);
2285 return biased_suffixes_[index];
2286 }
2287 inline int& shift(int index) {
2288 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2289 return biased_good_suffix_shift_[index];
2290 }
2291 private:
2292 int suffixes_[kBMMaxShift + 1];
2293 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002294 int* biased_suffixes_;
2295 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002296 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2297};
2298
2299// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002300static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002301static BMGoodSuffixBuffers bmgs_buffers;
2302
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002303// State of the string match tables.
2304// SIMPLE: No usable content in the buffers.
2305// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2306// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2307// Whenever starting with a new needle, one should call InitializeStringSearch
2308// to determine which search strategy to use, and in the case of a long-needle
2309// strategy, the call also initializes the algorithm to SIMPLE.
2310enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2311static StringSearchAlgorithm algorithm;
2312
2313
ager@chromium.org7c537e22008-10-16 08:43:32 +00002314// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002315template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002316static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2317 // Only preprocess at most kBMMaxShift last characters of pattern.
2318 int start = pattern.length() < kBMMaxShift ? 0
2319 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002320 // Run forwards to populate bad_char_table, so that *last* instance
2321 // of character equivalence class is the one registered.
2322 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002323 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2324 : kBMAlphabetSize;
2325 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002326 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002327 } else {
2328 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002329 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002330 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002331 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002332 for (int i = start; i < pattern.length() - 1; i++) {
2333 pchar c = pattern[i];
2334 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002335 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002336 }
2337}
2338
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002339
ager@chromium.org7c537e22008-10-16 08:43:32 +00002340template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002341static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002342 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002343 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002344 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002345 // Compute Good Suffix tables.
2346 bmgs_buffers.init(m);
2347
2348 bmgs_buffers.shift(m-1) = 1;
2349 bmgs_buffers.suffix(m) = m + 1;
2350 pchar last_char = pattern[m - 1];
2351 int suffix = m + 1;
2352 for (int i = m; i > start;) {
2353 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2354 if (bmgs_buffers.shift(suffix) == len) {
2355 bmgs_buffers.shift(suffix) = suffix - i;
2356 }
2357 suffix = bmgs_buffers.suffix(suffix);
2358 }
2359 i--;
2360 suffix--;
2361 bmgs_buffers.suffix(i) = suffix;
2362 if (suffix == m) {
2363 // No suffix to extend, so we check against last_char only.
2364 while (i > start && pattern[i - 1] != last_char) {
2365 if (bmgs_buffers.shift(m) == len) {
2366 bmgs_buffers.shift(m) = m - i;
2367 }
2368 i--;
2369 bmgs_buffers.suffix(i) = m;
2370 }
2371 if (i > start) {
2372 i--;
2373 suffix--;
2374 bmgs_buffers.suffix(i) = suffix;
2375 }
2376 }
2377 }
2378 if (suffix < m) {
2379 for (int i = start; i <= m; i++) {
2380 if (bmgs_buffers.shift(i) == len) {
2381 bmgs_buffers.shift(i) = suffix - start;
2382 }
2383 if (i == suffix) {
2384 suffix = bmgs_buffers.suffix(suffix);
2385 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002386 }
2387 }
2388}
2389
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002390
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002391template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002392static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002393 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002394 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002395 }
2396 if (sizeof(pchar) == 1) {
2397 if (char_code > String::kMaxAsciiCharCode) {
2398 return -1;
2399 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002400 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002401 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002402 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002403}
2404
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002405
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002406// Restricted simplified Boyer-Moore string matching.
2407// Uses only the bad-shift table of Boyer-Moore and only uses it
2408// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002409template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002410static int BoyerMooreHorspool(Vector<const schar> subject,
2411 Vector<const pchar> pattern,
2412 int start_index,
2413 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002414 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002415 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002416 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002417
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002418 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002419
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002420 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002421 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002422 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002423 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002424 // Perform search
2425 for (idx = start_index; idx <= n - m;) {
2426 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002427 int c;
2428 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002429 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002430 int shift = j - bc_occ;
2431 idx += shift;
2432 badness += 1 - shift; // at most zero, so badness cannot increase.
2433 if (idx > n - m) {
2434 *complete = true;
2435 return -1;
2436 }
2437 }
2438 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002439 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002440 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002441 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002442 return idx;
2443 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002444 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002445 // Badness increases by the number of characters we have
2446 // checked, and decreases by the number of characters we
2447 // can skip by shifting. It's a measure of how we are doing
2448 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002449 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002450 if (badness > 0) {
2451 *complete = false;
2452 return idx;
2453 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002454 }
2455 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002456 *complete = true;
2457 return -1;
2458}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002459
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002460
2461template <typename schar, typename pchar>
2462static int BoyerMooreIndexOf(Vector<const schar> subject,
2463 Vector<const pchar> pattern,
2464 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002465 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002466 int n = subject.length();
2467 int m = pattern.length();
2468 // Only preprocess at most kBMMaxShift last characters of pattern.
2469 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2470
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002471 pchar last_char = pattern[m - 1];
2472 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002473 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002474 int j = m - 1;
2475 schar c;
2476 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002477 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002478 idx += shift;
2479 if (idx > n - m) {
2480 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002481 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002482 }
2483 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2484 if (j < 0) {
2485 return idx;
2486 } else if (j < start) {
2487 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002488 // Fall back on BMH shift.
2489 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002490 } else {
2491 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002492 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002493 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002494 if (gs_shift > shift) {
2495 shift = gs_shift;
2496 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002497 idx += shift;
2498 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002499 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002500
2501 return -1;
2502}
2503
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002504
2505template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002506static inline int SingleCharIndexOf(Vector<const schar> string,
2507 schar pattern_char,
2508 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002509 if (sizeof(schar) == 1) {
2510 const schar* pos = reinterpret_cast<const schar*>(
2511 memchr(string.start() + start_index,
2512 pattern_char,
2513 string.length() - start_index));
2514 if (pos == NULL) return -1;
2515 return pos - string.start();
2516 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002517 for (int i = start_index, n = string.length(); i < n; i++) {
2518 if (pattern_char == string[i]) {
2519 return i;
2520 }
2521 }
2522 return -1;
2523}
2524
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002525
2526template <typename schar>
2527static int SingleCharLastIndexOf(Vector<const schar> string,
2528 schar pattern_char,
2529 int start_index) {
2530 for (int i = start_index; i >= 0; i--) {
2531 if (pattern_char == string[i]) {
2532 return i;
2533 }
2534 }
2535 return -1;
2536}
2537
2538
ager@chromium.org7c537e22008-10-16 08:43:32 +00002539// Trivial string search for shorter strings.
2540// On return, if "complete" is set to true, the return value is the
2541// final result of searching for the patter in the subject.
2542// If "complete" is set to false, the return value is the index where
2543// further checking should start, i.e., it's guaranteed that the pattern
2544// does not occur at a position prior to the returned index.
2545template <typename pchar, typename schar>
2546static int SimpleIndexOf(Vector<const schar> subject,
2547 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002548 int idx,
2549 bool* complete) {
2550 // Badness is a count of how much work we have done. When we have
2551 // done enough work we decide it's probably worth switching to a better
2552 // algorithm.
2553 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002554
ager@chromium.org7c537e22008-10-16 08:43:32 +00002555 // We know our pattern is at least 2 characters, we cache the first so
2556 // the common case of the first character not matching is faster.
2557 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002558 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2559 badness++;
2560 if (badness > 0) {
2561 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002562 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002563 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002564 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2565 const schar* pos = reinterpret_cast<const schar*>(
2566 memchr(subject.start() + i,
2567 pattern_first_char,
2568 n - i + 1));
2569 if (pos == NULL) {
2570 *complete = true;
2571 return -1;
2572 }
2573 i = pos - subject.start();
2574 } else {
2575 if (subject[i] != pattern_first_char) continue;
2576 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002577 int j = 1;
2578 do {
2579 if (pattern[j] != subject[i+j]) {
2580 break;
2581 }
2582 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002583 } while (j < pattern.length());
2584 if (j == pattern.length()) {
2585 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002586 return i;
2587 }
2588 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002589 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002590 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002591 return -1;
2592}
2593
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002594// Simple indexOf that never bails out. For short patterns only.
2595template <typename pchar, typename schar>
2596static int SimpleIndexOf(Vector<const schar> subject,
2597 Vector<const pchar> pattern,
2598 int idx) {
2599 pchar pattern_first_char = pattern[0];
2600 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002601 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2602 const schar* pos = reinterpret_cast<const schar*>(
2603 memchr(subject.start() + i,
2604 pattern_first_char,
2605 n - i + 1));
2606 if (pos == NULL) return -1;
2607 i = pos - subject.start();
2608 } else {
2609 if (subject[i] != pattern_first_char) continue;
2610 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002611 int j = 1;
2612 do {
2613 if (pattern[j] != subject[i+j]) {
2614 break;
2615 }
2616 j++;
2617 } while (j < pattern.length());
2618 if (j == pattern.length()) {
2619 return i;
2620 }
2621 }
2622 return -1;
2623}
2624
2625
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002626// Strategy for searching for a string in another string.
2627enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002628
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002629
2630template <typename pchar>
2631static inline StringSearchStrategy InitializeStringSearch(
2632 Vector<const pchar> pat, bool ascii_subject) {
2633 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002634 // We have an ASCII haystack and a non-ASCII needle. Check if there
2635 // really is a non-ASCII character in the needle and bail out if there
2636 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002637 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002638 for (int i = 0; i < pat.length(); i++) {
2639 uc16 c = pat[i];
2640 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002641 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002642 }
2643 }
2644 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002645 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002646 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002648 algorithm = SIMPLE_SEARCH;
2649 return SEARCH_LONG;
2650}
2651
2652
2653// Dispatch long needle searches to different algorithms.
2654template <typename schar, typename pchar>
2655static int ComplexIndexOf(Vector<const schar> sub,
2656 Vector<const pchar> pat,
2657 int start_index) {
2658 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002659 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002660 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002661 int idx = start_index;
2662 switch (algorithm) {
2663 case SIMPLE_SEARCH:
2664 idx = SimpleIndexOf(sub, pat, idx, &complete);
2665 if (complete) return idx;
2666 BoyerMoorePopulateBadCharTable(pat);
2667 algorithm = BOYER_MOORE_HORSPOOL;
2668 // FALLTHROUGH.
2669 case BOYER_MOORE_HORSPOOL:
2670 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2671 if (complete) return idx;
2672 // Build the Good Suffix table and continue searching.
2673 BoyerMoorePopulateGoodSuffixTable(pat);
2674 algorithm = BOYER_MOORE;
2675 // FALLTHROUGH.
2676 case BOYER_MOORE:
2677 return BoyerMooreIndexOf(sub, pat, idx);
2678 }
2679 UNREACHABLE();
2680 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002681}
2682
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002683
2684// Dispatch to different search strategies for a single search.
2685// If searching multiple times on the same needle, the search
2686// strategy should only be computed once and then dispatch to different
2687// loops.
2688template <typename schar, typename pchar>
2689static int StringSearch(Vector<const schar> sub,
2690 Vector<const pchar> pat,
2691 int start_index) {
2692 bool ascii_subject = (sizeof(schar) == 1);
2693 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2694 switch (strategy) {
2695 case SEARCH_FAIL: return -1;
2696 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2697 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2698 }
2699 UNREACHABLE();
2700 return -1;
2701}
2702
2703
ager@chromium.org7c537e22008-10-16 08:43:32 +00002704// Perform string match of pattern on subject, starting at start index.
2705// Caller must ensure that 0 <= start_index <= sub->length(),
2706// and should check that pat->length() + start_index <= sub->length()
2707int Runtime::StringMatch(Handle<String> sub,
2708 Handle<String> pat,
2709 int start_index) {
2710 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002711 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002712
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002713 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002714 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002715
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002716 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002717 if (start_index + pattern_length > subject_length) return -1;
2718
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002719 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002720 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002721 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002722
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002723 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002724 // character patterns linear search is necessary, so any smart
2725 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002726 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002727 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002728 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002729 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002730 if (pchar > String::kMaxAsciiCharCode) {
2731 return -1;
2732 }
2733 Vector<const char> ascii_vector =
2734 sub->ToAsciiVector().SubVector(start_index, subject_length);
2735 const void* pos = memchr(ascii_vector.start(),
2736 static_cast<const char>(pchar),
2737 static_cast<size_t>(ascii_vector.length()));
2738 if (pos == NULL) {
2739 return -1;
2740 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002741 return static_cast<int>(reinterpret_cast<const char*>(pos)
2742 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002743 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002744 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002745 }
2746
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002747 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002748 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002749 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002750
ager@chromium.org7c537e22008-10-16 08:43:32 +00002751 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2752 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002753 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002754 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002755 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002756 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002757 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002758 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002759 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002760 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002761 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002762 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002763 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002764 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002765}
2766
2767
2768static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002769 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002770 ASSERT(args.length() == 3);
2771
ager@chromium.org7c537e22008-10-16 08:43:32 +00002772 CONVERT_ARG_CHECKED(String, sub, 0);
2773 CONVERT_ARG_CHECKED(String, pat, 1);
2774
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002775 Object* index = args[2];
2776 uint32_t start_index;
2777 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2778
ager@chromium.org870a0b62008-11-04 11:43:05 +00002779 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002780 int position = Runtime::StringMatch(sub, pat, start_index);
2781 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002782}
2783
2784
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002785template <typename schar, typename pchar>
2786static int StringMatchBackwards(Vector<const schar> sub,
2787 Vector<const pchar> pat,
2788 int idx) {
2789 ASSERT(pat.length() >= 1);
2790 ASSERT(idx + pat.length() <= sub.length());
2791
2792 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2793 for (int i = 0; i < pat.length(); i++) {
2794 uc16 c = pat[i];
2795 if (c > String::kMaxAsciiCharCode) {
2796 return -1;
2797 }
2798 }
2799 }
2800
2801 pchar pattern_first_char = pat[0];
2802 for (int i = idx; i >= 0; i--) {
2803 if (sub[i] != pattern_first_char) continue;
2804 int j = 1;
2805 while (j < pat.length()) {
2806 if (pat[j] != sub[i+j]) {
2807 break;
2808 }
2809 j++;
2810 }
2811 if (j == pat.length()) {
2812 return i;
2813 }
2814 }
2815 return -1;
2816}
2817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002818static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002819 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002820 ASSERT(args.length() == 3);
2821
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002822 CONVERT_ARG_CHECKED(String, sub, 0);
2823 CONVERT_ARG_CHECKED(String, pat, 1);
2824
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002825 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002826 uint32_t start_index;
2827 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2828
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002829 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002830 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002831
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002832 if (start_index + pat_length > sub_length) {
2833 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002834 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002835
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002836 if (pat_length == 0) {
2837 return Smi::FromInt(start_index);
2838 }
2839
2840 if (!sub->IsFlat()) {
2841 FlattenString(sub);
2842 }
2843
2844 if (pat_length == 1) {
2845 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2846 if (sub->IsAsciiRepresentation()) {
2847 uc16 pchar = pat->Get(0);
2848 if (pchar > String::kMaxAsciiCharCode) {
2849 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002850 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002851 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2852 static_cast<char>(pat->Get(0)),
2853 start_index));
2854 } else {
2855 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2856 pat->Get(0),
2857 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002858 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002859 }
2860
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002861 if (!pat->IsFlat()) {
2862 FlattenString(pat);
2863 }
2864
2865 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2866
2867 int position = -1;
2868
2869 if (pat->IsAsciiRepresentation()) {
2870 Vector<const char> pat_vector = pat->ToAsciiVector();
2871 if (sub->IsAsciiRepresentation()) {
2872 position = StringMatchBackwards(sub->ToAsciiVector(),
2873 pat_vector,
2874 start_index);
2875 } else {
2876 position = StringMatchBackwards(sub->ToUC16Vector(),
2877 pat_vector,
2878 start_index);
2879 }
2880 } else {
2881 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2882 if (sub->IsAsciiRepresentation()) {
2883 position = StringMatchBackwards(sub->ToAsciiVector(),
2884 pat_vector,
2885 start_index);
2886 } else {
2887 position = StringMatchBackwards(sub->ToUC16Vector(),
2888 pat_vector,
2889 start_index);
2890 }
2891 }
2892
2893 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002894}
2895
2896
2897static Object* Runtime_StringLocaleCompare(Arguments args) {
2898 NoHandleAllocation ha;
2899 ASSERT(args.length() == 2);
2900
2901 CONVERT_CHECKED(String, str1, args[0]);
2902 CONVERT_CHECKED(String, str2, args[1]);
2903
2904 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002905 int str1_length = str1->length();
2906 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907
2908 // Decide trivial cases without flattening.
2909 if (str1_length == 0) {
2910 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2911 return Smi::FromInt(-str2_length);
2912 } else {
2913 if (str2_length == 0) return Smi::FromInt(str1_length);
2914 }
2915
2916 int end = str1_length < str2_length ? str1_length : str2_length;
2917
2918 // No need to flatten if we are going to find the answer on the first
2919 // character. At this point we know there is at least one character
2920 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002921 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002922 if (d != 0) return Smi::FromInt(d);
2923
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002924 str1->TryFlatten();
2925 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002926
2927 static StringInputBuffer buf1;
2928 static StringInputBuffer buf2;
2929
2930 buf1.Reset(str1);
2931 buf2.Reset(str2);
2932
2933 for (int i = 0; i < end; i++) {
2934 uint16_t char1 = buf1.GetNext();
2935 uint16_t char2 = buf2.GetNext();
2936 if (char1 != char2) return Smi::FromInt(char1 - char2);
2937 }
2938
2939 return Smi::FromInt(str1_length - str2_length);
2940}
2941
2942
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002943static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002944 NoHandleAllocation ha;
2945 ASSERT(args.length() == 3);
2946
2947 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002948 Object* from = args[1];
2949 Object* to = args[2];
2950 int start, end;
2951 // We have a fast integer-only case here to avoid a conversion to double in
2952 // the common case where from and to are Smis.
2953 if (from->IsSmi() && to->IsSmi()) {
2954 start = Smi::cast(from)->value();
2955 end = Smi::cast(to)->value();
2956 } else {
2957 CONVERT_DOUBLE_CHECKED(from_number, from);
2958 CONVERT_DOUBLE_CHECKED(to_number, to);
2959 start = FastD2I(from_number);
2960 end = FastD2I(to_number);
2961 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002962 RUNTIME_ASSERT(end >= start);
2963 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002964 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002965 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002966 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002967}
2968
2969
ager@chromium.org41826e72009-03-30 13:30:57 +00002970static Object* Runtime_StringMatch(Arguments args) {
2971 ASSERT_EQ(3, args.length());
2972
2973 CONVERT_ARG_CHECKED(String, subject, 0);
2974 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2975 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2976 HandleScope handles;
2977
2978 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2979
2980 if (match.is_null()) {
2981 return Failure::Exception();
2982 }
2983 if (match->IsNull()) {
2984 return Heap::null_value();
2985 }
2986 int length = subject->length();
2987
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002988 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002989 ZoneList<int> offsets(8);
2990 do {
2991 int start;
2992 int end;
2993 {
2994 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002995 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002996 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2997 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2998 }
2999 offsets.Add(start);
3000 offsets.Add(end);
3001 int index = start < end ? end : end + 1;
3002 if (index > length) break;
3003 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3004 if (match.is_null()) {
3005 return Failure::Exception();
3006 }
3007 } while (!match->IsNull());
3008 int matches = offsets.length() / 2;
3009 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3010 for (int i = 0; i < matches ; i++) {
3011 int from = offsets.at(i * 2);
3012 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003013 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003014 }
3015 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3016 result->set_length(Smi::FromInt(matches));
3017 return *result;
3018}
3019
3020
lrn@chromium.org25156de2010-04-06 13:10:27 +00003021// Two smis before and after the match, for very long strings.
3022const int kMaxBuilderEntriesPerRegExpMatch = 5;
3023
3024
3025static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3026 Handle<JSArray> last_match_info,
3027 int match_start,
3028 int match_end) {
3029 // Fill last_match_info with a single capture.
3030 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3031 AssertNoAllocation no_gc;
3032 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3033 RegExpImpl::SetLastCaptureCount(elements, 2);
3034 RegExpImpl::SetLastInput(elements, *subject);
3035 RegExpImpl::SetLastSubject(elements, *subject);
3036 RegExpImpl::SetCapture(elements, 0, match_start);
3037 RegExpImpl::SetCapture(elements, 1, match_end);
3038}
3039
3040
3041template <typename schar>
3042static bool SearchCharMultiple(Vector<schar> subject,
3043 String* pattern,
3044 schar pattern_char,
3045 FixedArrayBuilder* builder,
3046 int* match_pos) {
3047 // Position of last match.
3048 int pos = *match_pos;
3049 int subject_length = subject.length();
3050 while (pos < subject_length) {
3051 int match_end = pos + 1;
3052 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3053 *match_pos = pos;
3054 return false;
3055 }
3056 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3057 if (new_pos >= 0) {
3058 // Match has been found.
3059 if (new_pos > match_end) {
3060 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3061 }
3062 pos = new_pos;
3063 builder->Add(pattern);
3064 } else {
3065 break;
3066 }
3067 }
3068 if (pos + 1 < subject_length) {
3069 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3070 }
3071 *match_pos = pos;
3072 return true;
3073}
3074
3075
3076static bool SearchCharMultiple(Handle<String> subject,
3077 Handle<String> pattern,
3078 Handle<JSArray> last_match_info,
3079 FixedArrayBuilder* builder) {
3080 ASSERT(subject->IsFlat());
3081 ASSERT_EQ(1, pattern->length());
3082 uc16 pattern_char = pattern->Get(0);
3083 // Treating position before first as initial "previous match position".
3084 int match_pos = -1;
3085
3086 for (;;) { // Break when search complete.
3087 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3088 AssertNoAllocation no_gc;
3089 if (subject->IsAsciiRepresentation()) {
3090 if (pattern_char > String::kMaxAsciiCharCode) {
3091 break;
3092 }
3093 Vector<const char> subject_vector = subject->ToAsciiVector();
3094 char pattern_ascii_char = static_cast<char>(pattern_char);
3095 bool complete = SearchCharMultiple<const char>(subject_vector,
3096 *pattern,
3097 pattern_ascii_char,
3098 builder,
3099 &match_pos);
3100 if (complete) break;
3101 } else {
3102 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3103 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3104 *pattern,
3105 pattern_char,
3106 builder,
3107 &match_pos);
3108 if (complete) break;
3109 }
3110 }
3111
3112 if (match_pos >= 0) {
3113 SetLastMatchInfoNoCaptures(subject,
3114 last_match_info,
3115 match_pos,
3116 match_pos + 1);
3117 return true;
3118 }
3119 return false; // No matches at all.
3120}
3121
3122
3123template <typename schar, typename pchar>
3124static bool SearchStringMultiple(Vector<schar> subject,
3125 String* pattern,
3126 Vector<pchar> pattern_string,
3127 FixedArrayBuilder* builder,
3128 int* match_pos) {
3129 int pos = *match_pos;
3130 int subject_length = subject.length();
3131 int pattern_length = pattern_string.length();
3132 int max_search_start = subject_length - pattern_length;
3133 bool is_ascii = (sizeof(schar) == 1);
3134 StringSearchStrategy strategy =
3135 InitializeStringSearch(pattern_string, is_ascii);
3136 switch (strategy) {
3137 case SEARCH_FAIL: return false;
3138 case SEARCH_SHORT:
3139 while (pos <= max_search_start) {
3140 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3141 *match_pos = pos;
3142 return false;
3143 }
3144 // Position of end of previous match.
3145 int match_end = pos + pattern_length;
3146 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3147 if (new_pos >= 0) {
3148 // A match.
3149 if (new_pos > match_end) {
3150 ReplacementStringBuilder::AddSubjectSlice(builder,
3151 match_end,
3152 new_pos);
3153 }
3154 pos = new_pos;
3155 builder->Add(pattern);
3156 } else {
3157 break;
3158 }
3159 }
3160 break;
3161 case SEARCH_LONG:
3162 while (pos <= max_search_start) {
3163 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3164 *match_pos = pos;
3165 return false;
3166 }
3167 int new_pos = ComplexIndexOf(subject,
3168 pattern_string,
3169 pos + pattern_length);
3170 if (new_pos >= 0) {
3171 // A match has been found.
3172 if (new_pos > pos) {
3173 ReplacementStringBuilder::AddSubjectSlice(builder, pos, new_pos);
3174 }
3175 pos = new_pos;
3176 builder->Add(pattern);
3177 } else {
3178 break;
3179 }
3180 }
3181 break;
3182 }
3183 if (pos < max_search_start) {
3184 ReplacementStringBuilder::AddSubjectSlice(builder,
3185 pos + pattern_length,
3186 subject_length);
3187 }
3188 *match_pos = pos;
3189 return true;
3190}
3191
3192
3193static bool SearchStringMultiple(Handle<String> subject,
3194 Handle<String> pattern,
3195 Handle<JSArray> last_match_info,
3196 FixedArrayBuilder* builder) {
3197 ASSERT(subject->IsFlat());
3198 ASSERT(pattern->IsFlat());
3199 ASSERT(pattern->length() > 1);
3200
3201 // Treating as if a previous match was before first character.
3202 int match_pos = -pattern->length();
3203
3204 for (;;) { // Break when search complete.
3205 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3206 AssertNoAllocation no_gc;
3207 if (subject->IsAsciiRepresentation()) {
3208 Vector<const char> subject_vector = subject->ToAsciiVector();
3209 if (pattern->IsAsciiRepresentation()) {
3210 if (SearchStringMultiple(subject_vector,
3211 *pattern,
3212 pattern->ToAsciiVector(),
3213 builder,
3214 &match_pos)) break;
3215 } else {
3216 if (SearchStringMultiple(subject_vector,
3217 *pattern,
3218 pattern->ToUC16Vector(),
3219 builder,
3220 &match_pos)) break;
3221 }
3222 } else {
3223 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3224 if (pattern->IsAsciiRepresentation()) {
3225 if (SearchStringMultiple(subject_vector,
3226 *pattern,
3227 pattern->ToAsciiVector(),
3228 builder,
3229 &match_pos)) break;
3230 } else {
3231 if (SearchStringMultiple(subject_vector,
3232 *pattern,
3233 pattern->ToUC16Vector(),
3234 builder,
3235 &match_pos)) break;
3236 }
3237 }
3238 }
3239
3240 if (match_pos >= 0) {
3241 SetLastMatchInfoNoCaptures(subject,
3242 last_match_info,
3243 match_pos,
3244 match_pos + pattern->length());
3245 return true;
3246 }
3247 return false; // No matches at all.
3248}
3249
3250
3251static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3252 Handle<String> subject,
3253 Handle<JSRegExp> regexp,
3254 Handle<JSArray> last_match_array,
3255 FixedArrayBuilder* builder) {
3256 ASSERT(subject->IsFlat());
3257 int match_start = -1;
3258 int match_end = 0;
3259 int pos = 0;
3260 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3261 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3262
3263 OffsetsVector registers(required_registers);
3264 Vector<int> register_vector(registers.vector(), registers.length());
3265 int subject_length = subject->length();
3266
3267 for (;;) { // Break on failure, return on exception.
3268 RegExpImpl::IrregexpResult result =
3269 RegExpImpl::IrregexpExecOnce(regexp,
3270 subject,
3271 pos,
3272 register_vector);
3273 if (result == RegExpImpl::RE_SUCCESS) {
3274 match_start = register_vector[0];
3275 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3276 if (match_end < match_start) {
3277 ReplacementStringBuilder::AddSubjectSlice(builder,
3278 match_end,
3279 match_start);
3280 }
3281 match_end = register_vector[1];
3282 HandleScope loop_scope;
3283 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3284 if (match_start != match_end) {
3285 pos = match_end;
3286 } else {
3287 pos = match_end + 1;
3288 if (pos > subject_length) break;
3289 }
3290 } else if (result == RegExpImpl::RE_FAILURE) {
3291 break;
3292 } else {
3293 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3294 return result;
3295 }
3296 }
3297
3298 if (match_start >= 0) {
3299 if (match_end < subject_length) {
3300 ReplacementStringBuilder::AddSubjectSlice(builder,
3301 match_end,
3302 subject_length);
3303 }
3304 SetLastMatchInfoNoCaptures(subject,
3305 last_match_array,
3306 match_start,
3307 match_end);
3308 return RegExpImpl::RE_SUCCESS;
3309 } else {
3310 return RegExpImpl::RE_FAILURE; // No matches at all.
3311 }
3312}
3313
3314
3315static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3316 Handle<String> subject,
3317 Handle<JSRegExp> regexp,
3318 Handle<JSArray> last_match_array,
3319 FixedArrayBuilder* builder) {
3320
3321 ASSERT(subject->IsFlat());
3322 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3323 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3324
3325 OffsetsVector registers(required_registers);
3326 Vector<int> register_vector(registers.vector(), registers.length());
3327
3328 RegExpImpl::IrregexpResult result =
3329 RegExpImpl::IrregexpExecOnce(regexp,
3330 subject,
3331 0,
3332 register_vector);
3333
3334 int capture_count = regexp->CaptureCount();
3335 int subject_length = subject->length();
3336
3337 // Position to search from.
3338 int pos = 0;
3339 // End of previous match. Differs from pos if match was empty.
3340 int match_end = 0;
3341 if (result == RegExpImpl::RE_SUCCESS) {
3342 // Need to keep a copy of the previous match for creating last_match_info
3343 // at the end, so we have two vectors that we swap between.
3344 OffsetsVector registers2(required_registers);
3345 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3346
3347 do {
3348 int match_start = register_vector[0];
3349 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3350 if (match_end < match_start) {
3351 ReplacementStringBuilder::AddSubjectSlice(builder,
3352 match_end,
3353 match_start);
3354 }
3355 match_end = register_vector[1];
3356
3357 {
3358 // Avoid accumulating new handles inside loop.
3359 HandleScope temp_scope;
3360 // Arguments array to replace function is match, captures, index and
3361 // subject, i.e., 3 + capture count in total.
3362 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3363 elements->set(0, *Factory::NewSubString(subject,
3364 match_start,
3365 match_end));
3366 for (int i = 1; i <= capture_count; i++) {
3367 int start = register_vector[i * 2];
3368 if (start >= 0) {
3369 int end = register_vector[i * 2 + 1];
3370 ASSERT(start <= end);
3371 Handle<String> substring = Factory::NewSubString(subject,
3372 start,
3373 end);
3374 elements->set(i, *substring);
3375 } else {
3376 ASSERT(register_vector[i * 2 + 1] < 0);
3377 elements->set(i, Heap::undefined_value());
3378 }
3379 }
3380 elements->set(capture_count + 1, Smi::FromInt(match_start));
3381 elements->set(capture_count + 2, *subject);
3382 builder->Add(*Factory::NewJSArrayWithElements(elements));
3383 }
3384 // Swap register vectors, so the last successful match is in
3385 // prev_register_vector.
3386 Vector<int> tmp = prev_register_vector;
3387 prev_register_vector = register_vector;
3388 register_vector = tmp;
3389
3390 if (match_end > match_start) {
3391 pos = match_end;
3392 } else {
3393 pos = match_end + 1;
3394 if (pos > subject_length) {
3395 break;
3396 }
3397 }
3398
3399 result = RegExpImpl::IrregexpExecOnce(regexp,
3400 subject,
3401 pos,
3402 register_vector);
3403 } while (result == RegExpImpl::RE_SUCCESS);
3404
3405 if (result != RegExpImpl::RE_EXCEPTION) {
3406 // Finished matching, with at least one match.
3407 if (match_end < subject_length) {
3408 ReplacementStringBuilder::AddSubjectSlice(builder,
3409 match_end,
3410 subject_length);
3411 }
3412
3413 int last_match_capture_count = (capture_count + 1) * 2;
3414 int last_match_array_size =
3415 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3416 last_match_array->EnsureSize(last_match_array_size);
3417 AssertNoAllocation no_gc;
3418 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3419 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3420 RegExpImpl::SetLastSubject(elements, *subject);
3421 RegExpImpl::SetLastInput(elements, *subject);
3422 for (int i = 0; i < last_match_capture_count; i++) {
3423 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3424 }
3425 return RegExpImpl::RE_SUCCESS;
3426 }
3427 }
3428 // No matches at all, return failure or exception result directly.
3429 return result;
3430}
3431
3432
3433static Object* Runtime_RegExpExecMultiple(Arguments args) {
3434 ASSERT(args.length() == 4);
3435 HandleScope handles;
3436
3437 CONVERT_ARG_CHECKED(String, subject, 1);
3438 if (!subject->IsFlat()) { FlattenString(subject); }
3439 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3440 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3441 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3442
3443 ASSERT(last_match_info->HasFastElements());
3444 ASSERT(regexp->GetFlags().is_global());
3445 Handle<FixedArray> result_elements;
3446 if (result_array->HasFastElements()) {
3447 result_elements =
3448 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3449 } else {
3450 result_elements = Factory::NewFixedArrayWithHoles(16);
3451 }
3452 FixedArrayBuilder builder(result_elements);
3453
3454 if (regexp->TypeTag() == JSRegExp::ATOM) {
3455 Handle<String> pattern(
3456 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3457 int pattern_length = pattern->length();
3458 if (pattern_length == 1) {
3459 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3460 return *builder.ToJSArray(result_array);
3461 }
3462 return Heap::null_value();
3463 }
3464
3465 if (!pattern->IsFlat()) FlattenString(pattern);
3466 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3467 return *builder.ToJSArray(result_array);
3468 }
3469 return Heap::null_value();
3470 }
3471
3472 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3473
3474 RegExpImpl::IrregexpResult result;
3475 if (regexp->CaptureCount() == 0) {
3476 result = SearchRegExpNoCaptureMultiple(subject,
3477 regexp,
3478 last_match_info,
3479 &builder);
3480 } else {
3481 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3482 }
3483 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3484 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3485 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3486 return Failure::Exception();
3487}
3488
3489
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003490static Object* Runtime_NumberToRadixString(Arguments args) {
3491 NoHandleAllocation ha;
3492 ASSERT(args.length() == 2);
3493
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003494 // Fast case where the result is a one character string.
3495 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3496 int value = Smi::cast(args[0])->value();
3497 int radix = Smi::cast(args[1])->value();
3498 if (value >= 0 && value < radix) {
3499 RUNTIME_ASSERT(radix <= 36);
3500 // Character array used for conversion.
3501 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3502 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3503 }
3504 }
3505
3506 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003507 CONVERT_DOUBLE_CHECKED(value, args[0]);
3508 if (isnan(value)) {
3509 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3510 }
3511 if (isinf(value)) {
3512 if (value < 0) {
3513 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3514 }
3515 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3516 }
3517 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3518 int radix = FastD2I(radix_number);
3519 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3520 char* str = DoubleToRadixCString(value, radix);
3521 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3522 DeleteArray(str);
3523 return result;
3524}
3525
3526
3527static Object* Runtime_NumberToFixed(Arguments args) {
3528 NoHandleAllocation ha;
3529 ASSERT(args.length() == 2);
3530
3531 CONVERT_DOUBLE_CHECKED(value, args[0]);
3532 if (isnan(value)) {
3533 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3534 }
3535 if (isinf(value)) {
3536 if (value < 0) {
3537 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3538 }
3539 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3540 }
3541 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3542 int f = FastD2I(f_number);
3543 RUNTIME_ASSERT(f >= 0);
3544 char* str = DoubleToFixedCString(value, f);
3545 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3546 DeleteArray(str);
3547 return res;
3548}
3549
3550
3551static Object* Runtime_NumberToExponential(Arguments args) {
3552 NoHandleAllocation ha;
3553 ASSERT(args.length() == 2);
3554
3555 CONVERT_DOUBLE_CHECKED(value, args[0]);
3556 if (isnan(value)) {
3557 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3558 }
3559 if (isinf(value)) {
3560 if (value < 0) {
3561 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3562 }
3563 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3564 }
3565 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3566 int f = FastD2I(f_number);
3567 RUNTIME_ASSERT(f >= -1 && f <= 20);
3568 char* str = DoubleToExponentialCString(value, f);
3569 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3570 DeleteArray(str);
3571 return res;
3572}
3573
3574
3575static Object* Runtime_NumberToPrecision(Arguments args) {
3576 NoHandleAllocation ha;
3577 ASSERT(args.length() == 2);
3578
3579 CONVERT_DOUBLE_CHECKED(value, args[0]);
3580 if (isnan(value)) {
3581 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3582 }
3583 if (isinf(value)) {
3584 if (value < 0) {
3585 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3586 }
3587 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3588 }
3589 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3590 int f = FastD2I(f_number);
3591 RUNTIME_ASSERT(f >= 1 && f <= 21);
3592 char* str = DoubleToPrecisionCString(value, f);
3593 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3594 DeleteArray(str);
3595 return res;
3596}
3597
3598
3599// Returns a single character string where first character equals
3600// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003601static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003602 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003603 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003604 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003605 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003606 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003607 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003608}
3609
3610
3611Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3612 // Handle [] indexing on Strings
3613 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003614 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3615 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003616 }
3617
3618 // Handle [] indexing on String objects
3619 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003620 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3621 Handle<Object> result =
3622 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3623 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003624 }
3625
3626 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003627 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003628 return prototype->GetElement(index);
3629 }
3630
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003631 return GetElement(object, index);
3632}
3633
3634
3635Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003636 return object->GetElement(index);
3637}
3638
3639
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003640Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3641 HandleScope scope;
3642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003643 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003644 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003645 Handle<Object> error =
3646 Factory::NewTypeError("non_object_property_load",
3647 HandleVector(args, 2));
3648 return Top::Throw(*error);
3649 }
3650
3651 // Check if the given key is an array index.
3652 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003653 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003654 return GetElementOrCharAt(object, index);
3655 }
3656
3657 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003658 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003659 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003660 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003661 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003662 bool has_pending_exception = false;
3663 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003664 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003665 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003666 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003667 }
3668
ager@chromium.org32912102009-01-16 10:38:43 +00003669 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003670 // the element if so.
3671 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003672 return GetElementOrCharAt(object, index);
3673 } else {
3674 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003675 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003676 }
3677}
3678
3679
3680static Object* Runtime_GetProperty(Arguments args) {
3681 NoHandleAllocation ha;
3682 ASSERT(args.length() == 2);
3683
3684 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003685 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686
3687 return Runtime::GetObjectProperty(object, key);
3688}
3689
3690
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003691// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003692static Object* Runtime_KeyedGetProperty(Arguments args) {
3693 NoHandleAllocation ha;
3694 ASSERT(args.length() == 2);
3695
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003696 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003697 // itself.
3698 //
3699 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003700 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003701 // global proxy object never has properties. This is the case
3702 // because the global proxy object forwards everything to its hidden
3703 // prototype including local lookups.
3704 //
3705 // Additionally, we need to make sure that we do not cache results
3706 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003707 if (args[0]->IsJSObject() &&
3708 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003709 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003710 args[1]->IsString()) {
3711 JSObject* receiver = JSObject::cast(args[0]);
3712 String* key = String::cast(args[1]);
3713 if (receiver->HasFastProperties()) {
3714 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003715 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003716 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3717 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003718 Object* value = receiver->FastPropertyAt(offset);
3719 return value->IsTheHole() ? Heap::undefined_value() : value;
3720 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003721 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003722 LookupResult result;
3723 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003724 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003725 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003726 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003727 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003728 }
3729 } else {
3730 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003731 StringDictionary* dictionary = receiver->property_dictionary();
3732 int entry = dictionary->FindEntry(key);
3733 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003734 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003735 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003736 if (!receiver->IsGlobalObject()) return value;
3737 value = JSGlobalPropertyCell::cast(value)->value();
3738 if (!value->IsTheHole()) return value;
3739 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003740 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003741 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003742 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3743 // Fast case for string indexing using [] with a smi index.
3744 HandleScope scope;
3745 Handle<String> str = args.at<String>(0);
3746 int index = Smi::cast(args[1])->value();
3747 Handle<Object> result = GetCharAt(str, index);
3748 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003749 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003750
3751 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003752 return Runtime::GetObjectProperty(args.at<Object>(0),
3753 args.at<Object>(1));
3754}
3755
3756
ager@chromium.org5c838252010-02-19 08:53:10 +00003757static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3758 ASSERT(args.length() == 5);
3759 HandleScope scope;
3760 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3761 CONVERT_CHECKED(String, name, args[1]);
3762 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3763 CONVERT_CHECKED(JSFunction, fun, args[3]);
3764 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3765 int unchecked = flag_attr->value();
3766 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3767 RUNTIME_ASSERT(!obj->IsNull());
3768 LookupResult result;
3769 obj->LocalLookupRealNamedProperty(name, &result);
3770
3771 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3772 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3773 // delete it to avoid running into trouble in DefineAccessor, which
3774 // handles this incorrectly if the property is readonly (does nothing)
3775 if (result.IsProperty() &&
3776 (result.type() == FIELD || result.type() == NORMAL
3777 || result.type() == CONSTANT_FUNCTION)) {
3778 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3779 }
3780 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3781}
3782
3783static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3784 ASSERT(args.length() == 4);
3785 HandleScope scope;
3786 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3787 CONVERT_ARG_CHECKED(String, name, 1);
3788 Handle<Object> obj_value = args.at<Object>(2);
3789
3790 CONVERT_CHECKED(Smi, flag, args[3]);
3791 int unchecked = flag->value();
3792 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3793
3794 LookupResult result;
3795 js_object->LocalLookupRealNamedProperty(*name, &result);
3796
3797 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3798
3799 // Take special care when attributes are different and there is already
3800 // a property. For simplicity we normalize the property which enables us
3801 // to not worry about changing the instance_descriptor and creating a new
3802 // map. The current version of SetObjectProperty does not handle attributes
3803 // correctly in the case where a property is a field and is reset with
3804 // new attributes.
3805 if (result.IsProperty() && attr != result.GetAttributes()) {
3806 // New attributes - normalize to avoid writing to instance descriptor
3807 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3808 // Use IgnoreAttributes version since a readonly property may be
3809 // overridden and SetProperty does not allow this.
3810 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3811 *obj_value,
3812 attr);
3813 }
3814 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3815}
3816
3817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003818Object* Runtime::SetObjectProperty(Handle<Object> object,
3819 Handle<Object> key,
3820 Handle<Object> value,
3821 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003822 HandleScope scope;
3823
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003824 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003825 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003826 Handle<Object> error =
3827 Factory::NewTypeError("non_object_property_store",
3828 HandleVector(args, 2));
3829 return Top::Throw(*error);
3830 }
3831
3832 // If the object isn't a JavaScript object, we ignore the store.
3833 if (!object->IsJSObject()) return *value;
3834
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003835 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3836
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003837 // Check if the given key is an array index.
3838 uint32_t index;
3839 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003840 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3841 // of a string using [] notation. We need to support this too in
3842 // JavaScript.
3843 // In the case of a String object we just need to redirect the assignment to
3844 // the underlying string if the index is in range. Since the underlying
3845 // string does nothing with the assignment then we can ignore such
3846 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003847 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003848 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003849 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003850
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003851 Handle<Object> result = SetElement(js_object, index, value);
3852 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003853 return *value;
3854 }
3855
3856 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003857 Handle<Object> result;
3858 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003859 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003860 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003861 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003862 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003863 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003864 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003865 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866 return *value;
3867 }
3868
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003869 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003870 bool has_pending_exception = false;
3871 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3872 if (has_pending_exception) return Failure::Exception();
3873 Handle<String> name = Handle<String>::cast(converted);
3874
3875 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003876 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003877 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003878 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003879 }
3880}
3881
3882
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003883Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3884 Handle<Object> key,
3885 Handle<Object> value,
3886 PropertyAttributes attr) {
3887 HandleScope scope;
3888
3889 // Check if the given key is an array index.
3890 uint32_t index;
3891 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003892 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3893 // of a string using [] notation. We need to support this too in
3894 // JavaScript.
3895 // In the case of a String object we just need to redirect the assignment to
3896 // the underlying string if the index is in range. Since the underlying
3897 // string does nothing with the assignment then we can ignore such
3898 // assignments.
3899 if (js_object->IsStringObjectWithCharacterAt(index)) {
3900 return *value;
3901 }
3902
3903 return js_object->SetElement(index, *value);
3904 }
3905
3906 if (key->IsString()) {
3907 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003908 return js_object->SetElement(index, *value);
3909 } else {
3910 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003911 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003912 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3913 *value,
3914 attr);
3915 }
3916 }
3917
3918 // Call-back into JavaScript to convert the key to a string.
3919 bool has_pending_exception = false;
3920 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3921 if (has_pending_exception) return Failure::Exception();
3922 Handle<String> name = Handle<String>::cast(converted);
3923
3924 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003925 return js_object->SetElement(index, *value);
3926 } else {
3927 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3928 }
3929}
3930
3931
ager@chromium.orge2902be2009-06-08 12:21:35 +00003932Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3933 Handle<Object> key) {
3934 HandleScope scope;
3935
3936 // Check if the given key is an array index.
3937 uint32_t index;
3938 if (Array::IndexFromObject(*key, &index)) {
3939 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3940 // characters of a string using [] notation. In the case of a
3941 // String object we just need to redirect the deletion to the
3942 // underlying string if the index is in range. Since the
3943 // underlying string does nothing with the deletion, we can ignore
3944 // such deletions.
3945 if (js_object->IsStringObjectWithCharacterAt(index)) {
3946 return Heap::true_value();
3947 }
3948
3949 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3950 }
3951
3952 Handle<String> key_string;
3953 if (key->IsString()) {
3954 key_string = Handle<String>::cast(key);
3955 } else {
3956 // Call-back into JavaScript to convert the key to a string.
3957 bool has_pending_exception = false;
3958 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3959 if (has_pending_exception) return Failure::Exception();
3960 key_string = Handle<String>::cast(converted);
3961 }
3962
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003963 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003964 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3965}
3966
3967
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003968static Object* Runtime_SetProperty(Arguments args) {
3969 NoHandleAllocation ha;
3970 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3971
3972 Handle<Object> object = args.at<Object>(0);
3973 Handle<Object> key = args.at<Object>(1);
3974 Handle<Object> value = args.at<Object>(2);
3975
3976 // Compute attributes.
3977 PropertyAttributes attributes = NONE;
3978 if (args.length() == 4) {
3979 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003980 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003981 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003982 RUNTIME_ASSERT(
3983 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3984 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003985 }
3986 return Runtime::SetObjectProperty(object, key, value, attributes);
3987}
3988
3989
3990// Set a local property, even if it is READ_ONLY. If the property does not
3991// exist, it will be added with attributes NONE.
3992static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
3993 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003994 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003995 CONVERT_CHECKED(JSObject, object, args[0]);
3996 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003997 // Compute attributes.
3998 PropertyAttributes attributes = NONE;
3999 if (args.length() == 4) {
4000 CONVERT_CHECKED(Smi, value_obj, args[3]);
4001 int unchecked_value = value_obj->value();
4002 // Only attribute bits should be set.
4003 RUNTIME_ASSERT(
4004 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4005 attributes = static_cast<PropertyAttributes>(unchecked_value);
4006 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004007
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004008 return object->
4009 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004010}
4011
4012
4013static Object* Runtime_DeleteProperty(Arguments args) {
4014 NoHandleAllocation ha;
4015 ASSERT(args.length() == 2);
4016
4017 CONVERT_CHECKED(JSObject, object, args[0]);
4018 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004019 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004020}
4021
4022
ager@chromium.org9085a012009-05-11 19:22:57 +00004023static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4024 Handle<String> key) {
4025 if (object->HasLocalProperty(*key)) return Heap::true_value();
4026 // Handle hidden prototypes. If there's a hidden prototype above this thing
4027 // then we have to check it for properties, because they are supposed to
4028 // look like they are on this object.
4029 Handle<Object> proto(object->GetPrototype());
4030 if (proto->IsJSObject() &&
4031 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4032 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4033 }
4034 return Heap::false_value();
4035}
4036
4037
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004038static Object* Runtime_HasLocalProperty(Arguments args) {
4039 NoHandleAllocation ha;
4040 ASSERT(args.length() == 2);
4041 CONVERT_CHECKED(String, key, args[1]);
4042
ager@chromium.org9085a012009-05-11 19:22:57 +00004043 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004044 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004045 if (obj->IsJSObject()) {
4046 JSObject* object = JSObject::cast(obj);
4047 // Fast case - no interceptors.
4048 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4049 // Slow case. Either it's not there or we have an interceptor. We should
4050 // have handles for this kind of deal.
4051 HandleScope scope;
4052 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4053 Handle<String>(key));
4054 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004055 // Well, there is one exception: Handle [] on strings.
4056 uint32_t index;
4057 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004058 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004059 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004060 return Heap::true_value();
4061 }
4062 }
4063 return Heap::false_value();
4064}
4065
4066
4067static Object* Runtime_HasProperty(Arguments args) {
4068 NoHandleAllocation na;
4069 ASSERT(args.length() == 2);
4070
4071 // Only JS objects can have properties.
4072 if (args[0]->IsJSObject()) {
4073 JSObject* object = JSObject::cast(args[0]);
4074 CONVERT_CHECKED(String, key, args[1]);
4075 if (object->HasProperty(key)) return Heap::true_value();
4076 }
4077 return Heap::false_value();
4078}
4079
4080
4081static Object* Runtime_HasElement(Arguments args) {
4082 NoHandleAllocation na;
4083 ASSERT(args.length() == 2);
4084
4085 // Only JS objects can have elements.
4086 if (args[0]->IsJSObject()) {
4087 JSObject* object = JSObject::cast(args[0]);
4088 CONVERT_CHECKED(Smi, index_obj, args[1]);
4089 uint32_t index = index_obj->value();
4090 if (object->HasElement(index)) return Heap::true_value();
4091 }
4092 return Heap::false_value();
4093}
4094
4095
4096static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4097 NoHandleAllocation ha;
4098 ASSERT(args.length() == 2);
4099
4100 CONVERT_CHECKED(JSObject, object, args[0]);
4101 CONVERT_CHECKED(String, key, args[1]);
4102
4103 uint32_t index;
4104 if (key->AsArrayIndex(&index)) {
4105 return Heap::ToBoolean(object->HasElement(index));
4106 }
4107
ager@chromium.org870a0b62008-11-04 11:43:05 +00004108 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4109 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004110}
4111
4112
4113static Object* Runtime_GetPropertyNames(Arguments args) {
4114 HandleScope scope;
4115 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004116 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004117 return *GetKeysFor(object);
4118}
4119
4120
4121// Returns either a FixedArray as Runtime_GetPropertyNames,
4122// or, if the given object has an enum cache that contains
4123// all enumerable properties of the object and its prototypes
4124// have none, the map of the object. This is used to speed up
4125// the check for deletions during a for-in.
4126static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4127 ASSERT(args.length() == 1);
4128
4129 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4130
4131 if (raw_object->IsSimpleEnum()) return raw_object->map();
4132
4133 HandleScope scope;
4134 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004135 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4136 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137
4138 // Test again, since cache may have been built by preceding call.
4139 if (object->IsSimpleEnum()) return object->map();
4140
4141 return *content;
4142}
4143
4144
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004145// Find the length of the prototype chain that is to to handled as one. If a
4146// prototype object is hidden it is to be viewed as part of the the object it
4147// is prototype for.
4148static int LocalPrototypeChainLength(JSObject* obj) {
4149 int count = 1;
4150 Object* proto = obj->GetPrototype();
4151 while (proto->IsJSObject() &&
4152 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4153 count++;
4154 proto = JSObject::cast(proto)->GetPrototype();
4155 }
4156 return count;
4157}
4158
4159
4160// Return the names of the local named properties.
4161// args[0]: object
4162static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4163 HandleScope scope;
4164 ASSERT(args.length() == 1);
4165 if (!args[0]->IsJSObject()) {
4166 return Heap::undefined_value();
4167 }
4168 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4169
4170 // Skip the global proxy as it has no properties and always delegates to the
4171 // real global object.
4172 if (obj->IsJSGlobalProxy()) {
4173 // Only collect names if access is permitted.
4174 if (obj->IsAccessCheckNeeded() &&
4175 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4176 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4177 return *Factory::NewJSArray(0);
4178 }
4179 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4180 }
4181
4182 // Find the number of objects making up this.
4183 int length = LocalPrototypeChainLength(*obj);
4184
4185 // Find the number of local properties for each of the objects.
4186 int* local_property_count = NewArray<int>(length);
4187 int total_property_count = 0;
4188 Handle<JSObject> jsproto = obj;
4189 for (int i = 0; i < length; i++) {
4190 // Only collect names if access is permitted.
4191 if (jsproto->IsAccessCheckNeeded() &&
4192 !Top::MayNamedAccess(*jsproto,
4193 Heap::undefined_value(),
4194 v8::ACCESS_KEYS)) {
4195 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4196 return *Factory::NewJSArray(0);
4197 }
4198 int n;
4199 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4200 local_property_count[i] = n;
4201 total_property_count += n;
4202 if (i < length - 1) {
4203 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4204 }
4205 }
4206
4207 // Allocate an array with storage for all the property names.
4208 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4209
4210 // Get the property names.
4211 jsproto = obj;
4212 int proto_with_hidden_properties = 0;
4213 for (int i = 0; i < length; i++) {
4214 jsproto->GetLocalPropertyNames(*names,
4215 i == 0 ? 0 : local_property_count[i - 1]);
4216 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4217 proto_with_hidden_properties++;
4218 }
4219 if (i < length - 1) {
4220 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4221 }
4222 }
4223
4224 // Filter out name of hidden propeties object.
4225 if (proto_with_hidden_properties > 0) {
4226 Handle<FixedArray> old_names = names;
4227 names = Factory::NewFixedArray(
4228 names->length() - proto_with_hidden_properties);
4229 int dest_pos = 0;
4230 for (int i = 0; i < total_property_count; i++) {
4231 Object* name = old_names->get(i);
4232 if (name == Heap::hidden_symbol()) {
4233 continue;
4234 }
4235 names->set(dest_pos++, name);
4236 }
4237 }
4238
4239 DeleteArray(local_property_count);
4240 return *Factory::NewJSArrayWithElements(names);
4241}
4242
4243
4244// Return the names of the local indexed properties.
4245// args[0]: object
4246static Object* Runtime_GetLocalElementNames(Arguments args) {
4247 HandleScope scope;
4248 ASSERT(args.length() == 1);
4249 if (!args[0]->IsJSObject()) {
4250 return Heap::undefined_value();
4251 }
4252 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4253
4254 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4255 Handle<FixedArray> names = Factory::NewFixedArray(n);
4256 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4257 return *Factory::NewJSArrayWithElements(names);
4258}
4259
4260
4261// Return information on whether an object has a named or indexed interceptor.
4262// args[0]: object
4263static Object* Runtime_GetInterceptorInfo(Arguments args) {
4264 HandleScope scope;
4265 ASSERT(args.length() == 1);
4266 if (!args[0]->IsJSObject()) {
4267 return Smi::FromInt(0);
4268 }
4269 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4270
4271 int result = 0;
4272 if (obj->HasNamedInterceptor()) result |= 2;
4273 if (obj->HasIndexedInterceptor()) result |= 1;
4274
4275 return Smi::FromInt(result);
4276}
4277
4278
4279// Return property names from named interceptor.
4280// args[0]: object
4281static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4282 HandleScope scope;
4283 ASSERT(args.length() == 1);
4284 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4285
4286 if (obj->HasNamedInterceptor()) {
4287 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4288 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4289 }
4290 return Heap::undefined_value();
4291}
4292
4293
4294// Return element names from indexed interceptor.
4295// args[0]: object
4296static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4297 HandleScope scope;
4298 ASSERT(args.length() == 1);
4299 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4300
4301 if (obj->HasIndexedInterceptor()) {
4302 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4303 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4304 }
4305 return Heap::undefined_value();
4306}
4307
4308
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004309static Object* Runtime_LocalKeys(Arguments args) {
4310 ASSERT_EQ(args.length(), 1);
4311 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4312 HandleScope scope;
4313 Handle<JSObject> object(raw_object);
4314 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4315 LOCAL_ONLY);
4316 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4317 // property array and since the result is mutable we have to create
4318 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004319 int length = contents->length();
4320 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4321 for (int i = 0; i < length; i++) {
4322 Object* entry = contents->get(i);
4323 if (entry->IsString()) {
4324 copy->set(i, entry);
4325 } else {
4326 ASSERT(entry->IsNumber());
4327 HandleScope scope;
4328 Handle<Object> entry_handle(entry);
4329 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4330 copy->set(i, *entry_str);
4331 }
4332 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004333 return *Factory::NewJSArrayWithElements(copy);
4334}
4335
4336
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004337static Object* Runtime_GetArgumentsProperty(Arguments args) {
4338 NoHandleAllocation ha;
4339 ASSERT(args.length() == 1);
4340
4341 // Compute the frame holding the arguments.
4342 JavaScriptFrameIterator it;
4343 it.AdvanceToArgumentsFrame();
4344 JavaScriptFrame* frame = it.frame();
4345
4346 // Get the actual number of provided arguments.
4347 const uint32_t n = frame->GetProvidedParametersCount();
4348
4349 // Try to convert the key to an index. If successful and within
4350 // index return the the argument from the frame.
4351 uint32_t index;
4352 if (Array::IndexFromObject(args[0], &index) && index < n) {
4353 return frame->GetParameter(index);
4354 }
4355
4356 // Convert the key to a string.
4357 HandleScope scope;
4358 bool exception = false;
4359 Handle<Object> converted =
4360 Execution::ToString(args.at<Object>(0), &exception);
4361 if (exception) return Failure::Exception();
4362 Handle<String> key = Handle<String>::cast(converted);
4363
4364 // Try to convert the string key into an array index.
4365 if (key->AsArrayIndex(&index)) {
4366 if (index < n) {
4367 return frame->GetParameter(index);
4368 } else {
4369 return Top::initial_object_prototype()->GetElement(index);
4370 }
4371 }
4372
4373 // Handle special arguments properties.
4374 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4375 if (key->Equals(Heap::callee_symbol())) return frame->function();
4376
4377 // Lookup in the initial Object.prototype object.
4378 return Top::initial_object_prototype()->GetProperty(*key);
4379}
4380
4381
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004382static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004383 HandleScope scope;
4384
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004385 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004386 Handle<Object> object = args.at<Object>(0);
4387 if (object->IsJSObject()) {
4388 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004389 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4390 js_object->TransformToFastProperties(0);
4391 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004392 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004393 return *object;
4394}
4395
4396
4397static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004398 HandleScope scope;
4399
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004400 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004401 Handle<Object> object = args.at<Object>(0);
4402 if (object->IsJSObject()) {
4403 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004404 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004405 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004406 return *object;
4407}
4408
4409
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410static Object* Runtime_ToBool(Arguments args) {
4411 NoHandleAllocation ha;
4412 ASSERT(args.length() == 1);
4413
4414 return args[0]->ToBoolean();
4415}
4416
4417
4418// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4419// Possible optimizations: put the type string into the oddballs.
4420static Object* Runtime_Typeof(Arguments args) {
4421 NoHandleAllocation ha;
4422
4423 Object* obj = args[0];
4424 if (obj->IsNumber()) return Heap::number_symbol();
4425 HeapObject* heap_obj = HeapObject::cast(obj);
4426
4427 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004428 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004429
4430 InstanceType instance_type = heap_obj->map()->instance_type();
4431 if (instance_type < FIRST_NONSTRING_TYPE) {
4432 return Heap::string_symbol();
4433 }
4434
4435 switch (instance_type) {
4436 case ODDBALL_TYPE:
4437 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4438 return Heap::boolean_symbol();
4439 }
4440 if (heap_obj->IsNull()) {
4441 return Heap::object_symbol();
4442 }
4443 ASSERT(heap_obj->IsUndefined());
4444 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004445 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004446 return Heap::function_symbol();
4447 default:
4448 // For any kind of object not handled above, the spec rule for
4449 // host objects gives that it is okay to return "object"
4450 return Heap::object_symbol();
4451 }
4452}
4453
4454
lrn@chromium.org25156de2010-04-06 13:10:27 +00004455static bool AreDigits(const char*s, int from, int to) {
4456 for (int i = from; i < to; i++) {
4457 if (s[i] < '0' || s[i] > '9') return false;
4458 }
4459
4460 return true;
4461}
4462
4463
4464static int ParseDecimalInteger(const char*s, int from, int to) {
4465 ASSERT(to - from < 10); // Overflow is not possible.
4466 ASSERT(from < to);
4467 int d = s[from] - '0';
4468
4469 for (int i = from + 1; i < to; i++) {
4470 d = 10 * d + (s[i] - '0');
4471 }
4472
4473 return d;
4474}
4475
4476
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004477static Object* Runtime_StringToNumber(Arguments args) {
4478 NoHandleAllocation ha;
4479 ASSERT(args.length() == 1);
4480 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004481 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004482
4483 // Fast case: short integer or some sorts of junk values.
4484 int len = subject->length();
4485 if (subject->IsSeqAsciiString()) {
4486 if (len == 0) return Smi::FromInt(0);
4487
4488 char const* data = SeqAsciiString::cast(subject)->GetChars();
4489 bool minus = (data[0] == '-');
4490 int start_pos = (minus ? 1 : 0);
4491
4492 if (start_pos == len) {
4493 return Heap::nan_value();
4494 } else if (data[start_pos] > '9') {
4495 // Fast check for a junk value. A valid string may start from a
4496 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4497 // the 'I' character ('Infinity'). All of that have codes not greater than
4498 // '9' except 'I'.
4499 if (data[start_pos] != 'I') {
4500 return Heap::nan_value();
4501 }
4502 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4503 // The maximal/minimal smi has 10 digits. If the string has less digits we
4504 // know it will fit into the smi-data type.
4505 int d = ParseDecimalInteger(data, start_pos, len);
4506 if (minus) {
4507 if (d == 0) return Heap::minus_zero_value();
4508 d = -d;
4509 }
4510 return Smi::FromInt(d);
4511 }
4512 }
4513
4514 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004515 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4516}
4517
4518
4519static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4520 NoHandleAllocation ha;
4521 ASSERT(args.length() == 1);
4522
4523 CONVERT_CHECKED(JSArray, codes, args[0]);
4524 int length = Smi::cast(codes->length())->value();
4525
4526 // Check if the string can be ASCII.
4527 int i;
4528 for (i = 0; i < length; i++) {
4529 Object* element = codes->GetElement(i);
4530 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4531 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4532 break;
4533 }
4534
4535 Object* object = NULL;
4536 if (i == length) { // The string is ASCII.
4537 object = Heap::AllocateRawAsciiString(length);
4538 } else { // The string is not ASCII.
4539 object = Heap::AllocateRawTwoByteString(length);
4540 }
4541
4542 if (object->IsFailure()) return object;
4543 String* result = String::cast(object);
4544 for (int i = 0; i < length; i++) {
4545 Object* element = codes->GetElement(i);
4546 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004547 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004548 }
4549 return result;
4550}
4551
4552
4553// kNotEscaped is generated by the following:
4554//
4555// #!/bin/perl
4556// for (my $i = 0; $i < 256; $i++) {
4557// print "\n" if $i % 16 == 0;
4558// my $c = chr($i);
4559// my $escaped = 1;
4560// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4561// print $escaped ? "0, " : "1, ";
4562// }
4563
4564
4565static bool IsNotEscaped(uint16_t character) {
4566 // Only for 8 bit characters, the rest are always escaped (in a different way)
4567 ASSERT(character < 256);
4568 static const char kNotEscaped[256] = {
4569 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4570 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4572 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4573 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4574 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4575 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4576 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4577 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4578 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4579 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4581 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4583 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4584 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4585 };
4586 return kNotEscaped[character] != 0;
4587}
4588
4589
4590static Object* Runtime_URIEscape(Arguments args) {
4591 const char hex_chars[] = "0123456789ABCDEF";
4592 NoHandleAllocation ha;
4593 ASSERT(args.length() == 1);
4594 CONVERT_CHECKED(String, source, args[0]);
4595
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004596 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004597
4598 int escaped_length = 0;
4599 int length = source->length();
4600 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004601 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004602 buffer->Reset(source);
4603 while (buffer->has_more()) {
4604 uint16_t character = buffer->GetNext();
4605 if (character >= 256) {
4606 escaped_length += 6;
4607 } else if (IsNotEscaped(character)) {
4608 escaped_length++;
4609 } else {
4610 escaped_length += 3;
4611 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004612 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004613 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004614 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004615 Top::context()->mark_out_of_memory();
4616 return Failure::OutOfMemoryException();
4617 }
4618 }
4619 }
4620 // No length change implies no change. Return original string if no change.
4621 if (escaped_length == length) {
4622 return source;
4623 }
4624 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4625 if (o->IsFailure()) return o;
4626 String* destination = String::cast(o);
4627 int dest_position = 0;
4628
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004629 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004630 buffer->Rewind();
4631 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004632 uint16_t chr = buffer->GetNext();
4633 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004634 destination->Set(dest_position, '%');
4635 destination->Set(dest_position+1, 'u');
4636 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4637 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4638 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4639 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004640 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004641 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004642 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004643 dest_position++;
4644 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004645 destination->Set(dest_position, '%');
4646 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4647 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004648 dest_position += 3;
4649 }
4650 }
4651 return destination;
4652}
4653
4654
4655static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4656 static const signed char kHexValue['g'] = {
4657 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4658 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4659 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4660 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4661 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4662 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4663 -1, 10, 11, 12, 13, 14, 15 };
4664
4665 if (character1 > 'f') return -1;
4666 int hi = kHexValue[character1];
4667 if (hi == -1) return -1;
4668 if (character2 > 'f') return -1;
4669 int lo = kHexValue[character2];
4670 if (lo == -1) return -1;
4671 return (hi << 4) + lo;
4672}
4673
4674
ager@chromium.org870a0b62008-11-04 11:43:05 +00004675static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004676 int i,
4677 int length,
4678 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004679 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004680 int32_t hi = 0;
4681 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004682 if (character == '%' &&
4683 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004684 source->Get(i + 1) == 'u' &&
4685 (hi = TwoDigitHex(source->Get(i + 2),
4686 source->Get(i + 3))) != -1 &&
4687 (lo = TwoDigitHex(source->Get(i + 4),
4688 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004689 *step = 6;
4690 return (hi << 8) + lo;
4691 } else if (character == '%' &&
4692 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004693 (lo = TwoDigitHex(source->Get(i + 1),
4694 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004695 *step = 3;
4696 return lo;
4697 } else {
4698 *step = 1;
4699 return character;
4700 }
4701}
4702
4703
4704static Object* Runtime_URIUnescape(Arguments args) {
4705 NoHandleAllocation ha;
4706 ASSERT(args.length() == 1);
4707 CONVERT_CHECKED(String, source, args[0]);
4708
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004709 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004710
4711 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004712 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004713
4714 int unescaped_length = 0;
4715 for (int i = 0; i < length; unescaped_length++) {
4716 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004717 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004718 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004719 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004720 i += step;
4721 }
4722
4723 // No length change implies no change. Return original string if no change.
4724 if (unescaped_length == length)
4725 return source;
4726
4727 Object* o = ascii ?
4728 Heap::AllocateRawAsciiString(unescaped_length) :
4729 Heap::AllocateRawTwoByteString(unescaped_length);
4730 if (o->IsFailure()) return o;
4731 String* destination = String::cast(o);
4732
4733 int dest_position = 0;
4734 for (int i = 0; i < length; dest_position++) {
4735 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004736 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004737 i += step;
4738 }
4739 return destination;
4740}
4741
4742
4743static Object* Runtime_StringParseInt(Arguments args) {
4744 NoHandleAllocation ha;
4745
4746 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004747 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004748
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004749 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004750
lrn@chromium.org25156de2010-04-06 13:10:27 +00004751 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4752 double value = StringToInt(s, radix);
4753 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004754 return Heap::nan_value();
4755}
4756
4757
4758static Object* Runtime_StringParseFloat(Arguments args) {
4759 NoHandleAllocation ha;
4760 CONVERT_CHECKED(String, str, args[0]);
4761
4762 // ECMA-262 section 15.1.2.3, empty string is NaN
4763 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4764
4765 // Create a number object from the value.
4766 return Heap::NumberFromDouble(value);
4767}
4768
4769
4770static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4771static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4772
4773
4774template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004775static Object* ConvertCaseHelper(String* s,
4776 int length,
4777 int input_string_length,
4778 unibrow::Mapping<Converter, 128>* mapping) {
4779 // We try this twice, once with the assumption that the result is no longer
4780 // than the input and, if that assumption breaks, again with the exact
4781 // length. This may not be pretty, but it is nicer than what was here before
4782 // and I hereby claim my vaffel-is.
4783 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784 // Allocate the resulting string.
4785 //
4786 // NOTE: This assumes that the upper/lower case of an ascii
4787 // character is also ascii. This is currently the case, but it
4788 // might break in the future if we implement more context and locale
4789 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004790 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004791 ? Heap::AllocateRawAsciiString(length)
4792 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004793 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004794 String* result = String::cast(o);
4795 bool has_changed_character = false;
4796
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004797 // Convert all characters to upper case, assuming that they will fit
4798 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004799 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004800 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004801 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802 // We can assume that the string is not empty
4803 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004804 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004805 bool has_next = buffer->has_more();
4806 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004807 int char_length = mapping->get(current, next, chars);
4808 if (char_length == 0) {
4809 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004810 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004811 i++;
4812 } else if (char_length == 1) {
4813 // Common case: converting the letter resulted in one character.
4814 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004815 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004816 has_changed_character = true;
4817 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004818 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004819 // We've assumed that the result would be as long as the
4820 // input but here is a character that converts to several
4821 // characters. No matter, we calculate the exact length
4822 // of the result and try the whole thing again.
4823 //
4824 // Note that this leaves room for optimization. We could just
4825 // memcpy what we already have to the result string. Also,
4826 // the result string is the last object allocated we could
4827 // "realloc" it and probably, in the vast majority of cases,
4828 // extend the existing string to be able to hold the full
4829 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004830 int next_length = 0;
4831 if (has_next) {
4832 next_length = mapping->get(next, 0, chars);
4833 if (next_length == 0) next_length = 1;
4834 }
4835 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004836 while (buffer->has_more()) {
4837 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004838 // NOTE: we use 0 as the next character here because, while
4839 // the next character may affect what a character converts to,
4840 // it does not in any case affect the length of what it convert
4841 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004842 int char_length = mapping->get(current, 0, chars);
4843 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004844 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004845 if (current_length > Smi::kMaxValue) {
4846 Top::context()->mark_out_of_memory();
4847 return Failure::OutOfMemoryException();
4848 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004849 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004850 // Try again with the real length.
4851 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852 } else {
4853 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004854 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004855 i++;
4856 }
4857 has_changed_character = true;
4858 }
4859 current = next;
4860 }
4861 if (has_changed_character) {
4862 return result;
4863 } else {
4864 // If we didn't actually change anything in doing the conversion
4865 // we simple return the result and let the converted string
4866 // become garbage; there is no reason to keep two identical strings
4867 // alive.
4868 return s;
4869 }
4870}
4871
4872
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004873static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4874 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4875 if (s->IsConsString()) {
4876 ASSERT(ConsString::cast(s)->second()->length() == 0);
4877 return SeqAsciiString::cast(ConsString::cast(s)->first());
4878 }
4879 return SeqAsciiString::cast(s);
4880}
4881
4882
4883namespace {
4884
4885struct ToLowerTraits {
4886 typedef unibrow::ToLowercase UnibrowConverter;
4887
4888 static bool ConvertAscii(char* dst, char* src, int length) {
4889 bool changed = false;
4890 for (int i = 0; i < length; ++i) {
4891 char c = src[i];
4892 if ('A' <= c && c <= 'Z') {
4893 c += ('a' - 'A');
4894 changed = true;
4895 }
4896 dst[i] = c;
4897 }
4898 return changed;
4899 }
4900};
4901
4902
4903struct ToUpperTraits {
4904 typedef unibrow::ToUppercase UnibrowConverter;
4905
4906 static bool ConvertAscii(char* dst, char* src, int length) {
4907 bool changed = false;
4908 for (int i = 0; i < length; ++i) {
4909 char c = src[i];
4910 if ('a' <= c && c <= 'z') {
4911 c -= ('a' - 'A');
4912 changed = true;
4913 }
4914 dst[i] = c;
4915 }
4916 return changed;
4917 }
4918};
4919
4920} // namespace
4921
4922
4923template <typename ConvertTraits>
4924static Object* ConvertCase(
4925 Arguments args,
4926 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004927 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004928 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004929 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004930
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004931 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004932 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004933 if (length == 0) return s;
4934
4935 // Simpler handling of ascii strings.
4936 //
4937 // NOTE: This assumes that the upper/lower case of an ascii
4938 // character is also ascii. This is currently the case, but it
4939 // might break in the future if we implement more context and locale
4940 // dependent upper/lower conversions.
4941 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4942 if (seq_ascii != NULL) {
4943 Object* o = Heap::AllocateRawAsciiString(length);
4944 if (o->IsFailure()) return o;
4945 SeqAsciiString* result = SeqAsciiString::cast(o);
4946 bool has_changed_character = ConvertTraits::ConvertAscii(
4947 result->GetChars(), seq_ascii->GetChars(), length);
4948 return has_changed_character ? result : s;
4949 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004950
4951 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4952 if (answer->IsSmi()) {
4953 // Retry with correct length.
4954 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4955 }
4956 return answer; // This may be a failure.
4957}
4958
4959
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004960static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004961 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004962}
4963
4964
4965static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004966 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004967}
4968
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004969
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004970static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
4971 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
4972}
4973
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004974
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004975static Object* Runtime_StringTrim(Arguments args) {
4976 NoHandleAllocation ha;
4977 ASSERT(args.length() == 3);
4978
4979 CONVERT_CHECKED(String, s, args[0]);
4980 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
4981 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
4982
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004983 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004984 int length = s->length();
4985
4986 int left = 0;
4987 if (trimLeft) {
4988 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
4989 left++;
4990 }
4991 }
4992
4993 int right = length;
4994 if (trimRight) {
4995 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
4996 right--;
4997 }
4998 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004999 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005000}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005001
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005002
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005003template <typename schar, typename pchar>
5004void FindStringIndices(Vector<const schar> subject,
5005 Vector<const pchar> pattern,
5006 ZoneList<int>* indices,
5007 unsigned int limit) {
5008 ASSERT(limit > 0);
5009 // Collect indices of pattern in subject, and the end-of-string index.
5010 // Stop after finding at most limit values.
5011 StringSearchStrategy strategy =
5012 InitializeStringSearch(pattern, sizeof(schar) == 1);
5013 switch (strategy) {
5014 case SEARCH_FAIL: return;
5015 case SEARCH_SHORT: {
5016 int pattern_length = pattern.length();
5017 int index = 0;
5018 while (limit > 0) {
5019 index = SimpleIndexOf(subject, pattern, index);
5020 if (index < 0) return;
5021 indices->Add(index);
5022 index += pattern_length;
5023 limit--;
5024 }
5025 return;
5026 }
5027 case SEARCH_LONG: {
5028 int pattern_length = pattern.length();
5029 int index = 0;
5030 while (limit > 0) {
5031 index = ComplexIndexOf(subject, pattern, index);
5032 if (index < 0) return;
5033 indices->Add(index);
5034 index += pattern_length;
5035 limit--;
5036 }
5037 return;
5038 }
5039 default:
5040 UNREACHABLE();
5041 return;
5042 }
5043}
5044
5045template <typename schar>
5046inline void FindCharIndices(Vector<const schar> subject,
5047 const schar pattern_char,
5048 ZoneList<int>* indices,
5049 unsigned int limit) {
5050 // Collect indices of pattern_char in subject, and the end-of-string index.
5051 // Stop after finding at most limit values.
5052 int index = 0;
5053 while (limit > 0) {
5054 index = SingleCharIndexOf(subject, pattern_char, index);
5055 if (index < 0) return;
5056 indices->Add(index);
5057 index++;
5058 limit--;
5059 }
5060}
5061
5062
5063static Object* Runtime_StringSplit(Arguments args) {
5064 ASSERT(args.length() == 3);
5065 HandleScope handle_scope;
5066 CONVERT_ARG_CHECKED(String, subject, 0);
5067 CONVERT_ARG_CHECKED(String, pattern, 1);
5068 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5069
5070 int subject_length = subject->length();
5071 int pattern_length = pattern->length();
5072 RUNTIME_ASSERT(pattern_length > 0);
5073
5074 // The limit can be very large (0xffffffffu), but since the pattern
5075 // isn't empty, we can never create more parts than ~half the length
5076 // of the subject.
5077
5078 if (!subject->IsFlat()) FlattenString(subject);
5079
5080 static const int kMaxInitialListCapacity = 16;
5081
5082 ZoneScope scope(DELETE_ON_EXIT);
5083
5084 // Find (up to limit) indices of separator and end-of-string in subject
5085 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5086 ZoneList<int> indices(initial_capacity);
5087 if (pattern_length == 1) {
5088 // Special case, go directly to fast single-character split.
5089 AssertNoAllocation nogc;
5090 uc16 pattern_char = pattern->Get(0);
5091 if (subject->IsTwoByteRepresentation()) {
5092 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5093 &indices,
5094 limit);
5095 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5096 FindCharIndices(subject->ToAsciiVector(),
5097 static_cast<char>(pattern_char),
5098 &indices,
5099 limit);
5100 }
5101 } else {
5102 if (!pattern->IsFlat()) FlattenString(pattern);
5103 AssertNoAllocation nogc;
5104 if (subject->IsAsciiRepresentation()) {
5105 Vector<const char> subject_vector = subject->ToAsciiVector();
5106 if (pattern->IsAsciiRepresentation()) {
5107 FindStringIndices(subject_vector,
5108 pattern->ToAsciiVector(),
5109 &indices,
5110 limit);
5111 } else {
5112 FindStringIndices(subject_vector,
5113 pattern->ToUC16Vector(),
5114 &indices,
5115 limit);
5116 }
5117 } else {
5118 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5119 if (pattern->IsAsciiRepresentation()) {
5120 FindStringIndices(subject_vector,
5121 pattern->ToAsciiVector(),
5122 &indices,
5123 limit);
5124 } else {
5125 FindStringIndices(subject_vector,
5126 pattern->ToUC16Vector(),
5127 &indices,
5128 limit);
5129 }
5130 }
5131 }
5132 if (static_cast<uint32_t>(indices.length()) < limit) {
5133 indices.Add(subject_length);
5134 }
5135 // The list indices now contains the end of each part to create.
5136
5137
5138 // Create JSArray of substrings separated by separator.
5139 int part_count = indices.length();
5140
5141 Handle<JSArray> result = Factory::NewJSArray(part_count);
5142 result->set_length(Smi::FromInt(part_count));
5143
5144 ASSERT(result->HasFastElements());
5145
5146 if (part_count == 1 && indices.at(0) == subject_length) {
5147 FixedArray::cast(result->elements())->set(0, *subject);
5148 return *result;
5149 }
5150
5151 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5152 int part_start = 0;
5153 for (int i = 0; i < part_count; i++) {
5154 HandleScope local_loop_handle;
5155 int part_end = indices.at(i);
5156 Handle<String> substring =
5157 Factory::NewSubString(subject, part_start, part_end);
5158 elements->set(i, *substring);
5159 part_start = part_end + pattern_length;
5160 }
5161
5162 return *result;
5163}
5164
5165
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005166// Copies ascii characters to the given fixed array looking up
5167// one-char strings in the cache. Gives up on the first char that is
5168// not in the cache and fills the remainder with smi zeros. Returns
5169// the length of the successfully copied prefix.
5170static int CopyCachedAsciiCharsToArray(const char* chars,
5171 FixedArray* elements,
5172 int length) {
5173 AssertNoAllocation nogc;
5174 FixedArray* ascii_cache = Heap::single_character_string_cache();
5175 Object* undefined = Heap::undefined_value();
5176 int i;
5177 for (i = 0; i < length; ++i) {
5178 Object* value = ascii_cache->get(chars[i]);
5179 if (value == undefined) break;
5180 ASSERT(!Heap::InNewSpace(value));
5181 elements->set(i, value, SKIP_WRITE_BARRIER);
5182 }
5183 if (i < length) {
5184 ASSERT(Smi::FromInt(0) == 0);
5185 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5186 }
5187#ifdef DEBUG
5188 for (int j = 0; j < length; ++j) {
5189 Object* element = elements->get(j);
5190 ASSERT(element == Smi::FromInt(0) ||
5191 (element->IsString() && String::cast(element)->LooksValid()));
5192 }
5193#endif
5194 return i;
5195}
5196
5197
5198// Converts a String to JSArray.
5199// For example, "foo" => ["f", "o", "o"].
5200static Object* Runtime_StringToArray(Arguments args) {
5201 HandleScope scope;
5202 ASSERT(args.length() == 1);
5203 CONVERT_ARG_CHECKED(String, s, 0);
5204
5205 s->TryFlatten();
5206 const int length = s->length();
5207
5208 Handle<FixedArray> elements;
5209 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5210 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5211 if (obj->IsFailure()) return obj;
5212 elements = Handle<FixedArray>(FixedArray::cast(obj));
5213
5214 Vector<const char> chars = s->ToAsciiVector();
5215 // Note, this will initialize all elements (not only the prefix)
5216 // to prevent GC from seeing partially initialized array.
5217 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5218 *elements,
5219 length);
5220
5221 for (int i = num_copied_from_cache; i < length; ++i) {
5222 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5223 }
5224 } else {
5225 elements = Factory::NewFixedArray(length);
5226 for (int i = 0; i < length; ++i) {
5227 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5228 }
5229 }
5230
5231#ifdef DEBUG
5232 for (int i = 0; i < length; ++i) {
5233 ASSERT(String::cast(elements->get(i))->length() == 1);
5234 }
5235#endif
5236
5237 return *Factory::NewJSArrayWithElements(elements);
5238}
5239
5240
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005241bool Runtime::IsUpperCaseChar(uint16_t ch) {
5242 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5243 int char_length = to_upper_mapping.get(ch, 0, chars);
5244 return char_length == 0;
5245}
5246
5247
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005248static Object* Runtime_NumberToString(Arguments args) {
5249 NoHandleAllocation ha;
5250 ASSERT(args.length() == 1);
5251
5252 Object* number = args[0];
5253 RUNTIME_ASSERT(number->IsNumber());
5254
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005255 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005256}
5257
5258
ager@chromium.org357bf652010-04-12 11:30:10 +00005259static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5260 NoHandleAllocation ha;
5261 ASSERT(args.length() == 1);
5262
5263 Object* number = args[0];
5264 RUNTIME_ASSERT(number->IsNumber());
5265
5266 return Heap::NumberToString(number, false);
5267}
5268
5269
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005270static Object* Runtime_NumberToInteger(Arguments args) {
5271 NoHandleAllocation ha;
5272 ASSERT(args.length() == 1);
5273
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005274 CONVERT_DOUBLE_CHECKED(number, args[0]);
5275
5276 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5277 if (number > 0 && number <= Smi::kMaxValue) {
5278 return Smi::FromInt(static_cast<int>(number));
5279 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005280 return Heap::NumberFromDouble(DoubleToInteger(number));
5281}
5282
5283
5284static Object* Runtime_NumberToJSUint32(Arguments args) {
5285 NoHandleAllocation ha;
5286 ASSERT(args.length() == 1);
5287
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005288 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005289 return Heap::NumberFromUint32(number);
5290}
5291
5292
5293static Object* Runtime_NumberToJSInt32(Arguments args) {
5294 NoHandleAllocation ha;
5295 ASSERT(args.length() == 1);
5296
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005297 CONVERT_DOUBLE_CHECKED(number, args[0]);
5298
5299 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5300 if (number > 0 && number <= Smi::kMaxValue) {
5301 return Smi::FromInt(static_cast<int>(number));
5302 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005303 return Heap::NumberFromInt32(DoubleToInt32(number));
5304}
5305
5306
ager@chromium.org870a0b62008-11-04 11:43:05 +00005307// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5308// a small integer.
5309static Object* Runtime_NumberToSmi(Arguments args) {
5310 NoHandleAllocation ha;
5311 ASSERT(args.length() == 1);
5312
5313 Object* obj = args[0];
5314 if (obj->IsSmi()) {
5315 return obj;
5316 }
5317 if (obj->IsHeapNumber()) {
5318 double value = HeapNumber::cast(obj)->value();
5319 int int_value = FastD2I(value);
5320 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5321 return Smi::FromInt(int_value);
5322 }
5323 }
5324 return Heap::nan_value();
5325}
5326
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005327
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005328static Object* Runtime_NumberAdd(Arguments args) {
5329 NoHandleAllocation ha;
5330 ASSERT(args.length() == 2);
5331
5332 CONVERT_DOUBLE_CHECKED(x, args[0]);
5333 CONVERT_DOUBLE_CHECKED(y, args[1]);
5334 return Heap::AllocateHeapNumber(x + y);
5335}
5336
5337
5338static Object* Runtime_NumberSub(Arguments args) {
5339 NoHandleAllocation ha;
5340 ASSERT(args.length() == 2);
5341
5342 CONVERT_DOUBLE_CHECKED(x, args[0]);
5343 CONVERT_DOUBLE_CHECKED(y, args[1]);
5344 return Heap::AllocateHeapNumber(x - y);
5345}
5346
5347
5348static Object* Runtime_NumberMul(Arguments args) {
5349 NoHandleAllocation ha;
5350 ASSERT(args.length() == 2);
5351
5352 CONVERT_DOUBLE_CHECKED(x, args[0]);
5353 CONVERT_DOUBLE_CHECKED(y, args[1]);
5354 return Heap::AllocateHeapNumber(x * y);
5355}
5356
5357
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005358static Object* Runtime_NumberUnaryMinus(Arguments args) {
5359 NoHandleAllocation ha;
5360 ASSERT(args.length() == 1);
5361
5362 CONVERT_DOUBLE_CHECKED(x, args[0]);
5363 return Heap::AllocateHeapNumber(-x);
5364}
5365
5366
5367static Object* Runtime_NumberDiv(Arguments args) {
5368 NoHandleAllocation ha;
5369 ASSERT(args.length() == 2);
5370
5371 CONVERT_DOUBLE_CHECKED(x, args[0]);
5372 CONVERT_DOUBLE_CHECKED(y, args[1]);
5373 return Heap::NewNumberFromDouble(x / y);
5374}
5375
5376
5377static Object* Runtime_NumberMod(Arguments args) {
5378 NoHandleAllocation ha;
5379 ASSERT(args.length() == 2);
5380
5381 CONVERT_DOUBLE_CHECKED(x, args[0]);
5382 CONVERT_DOUBLE_CHECKED(y, args[1]);
5383
ager@chromium.org3811b432009-10-28 14:53:37 +00005384 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005385 // NewNumberFromDouble may return a Smi instead of a Number object
5386 return Heap::NewNumberFromDouble(x);
5387}
5388
5389
5390static Object* Runtime_StringAdd(Arguments args) {
5391 NoHandleAllocation ha;
5392 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005393 CONVERT_CHECKED(String, str1, args[0]);
5394 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005395 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005396 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005397}
5398
5399
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005400template<typename sinkchar>
5401static inline void StringBuilderConcatHelper(String* special,
5402 sinkchar* sink,
5403 FixedArray* fixed_array,
5404 int array_length) {
5405 int position = 0;
5406 for (int i = 0; i < array_length; i++) {
5407 Object* element = fixed_array->get(i);
5408 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005409 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005410 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005411 int pos;
5412 int len;
5413 if (encoded_slice > 0) {
5414 // Position and length encoded in one smi.
5415 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5416 len = StringBuilderSubstringLength::decode(encoded_slice);
5417 } else {
5418 // Position and length encoded in two smis.
5419 Object* obj = fixed_array->get(++i);
5420 ASSERT(obj->IsSmi());
5421 pos = Smi::cast(obj)->value();
5422 len = -encoded_slice;
5423 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005424 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005425 sink + position,
5426 pos,
5427 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005428 position += len;
5429 } else {
5430 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005431 int element_length = string->length();
5432 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005433 position += element_length;
5434 }
5435 }
5436}
5437
5438
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005439static Object* Runtime_StringBuilderConcat(Arguments args) {
5440 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005441 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005442 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005443 if (!args[1]->IsSmi()) {
5444 Top::context()->mark_out_of_memory();
5445 return Failure::OutOfMemoryException();
5446 }
5447 int array_length = Smi::cast(args[1])->value();
5448 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005449
5450 // This assumption is used by the slice encoding in one or two smis.
5451 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5452
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005453 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005454 if (!array->HasFastElements()) {
5455 return Top::Throw(Heap::illegal_argument_symbol());
5456 }
5457 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005458 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005459 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005460 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005461
5462 if (array_length == 0) {
5463 return Heap::empty_string();
5464 } else if (array_length == 1) {
5465 Object* first = fixed_array->get(0);
5466 if (first->IsString()) return first;
5467 }
5468
ager@chromium.org5ec48922009-05-05 07:25:34 +00005469 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005470 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005471 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005472 for (int i = 0; i < array_length; i++) {
5473 Object* elt = fixed_array->get(i);
5474 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005475 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005476 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005477 if (len > 0) {
5478 // Position and length encoded in one smi.
5479 int pos = len >> 11;
5480 len &= 0x7ff;
5481 if (pos + len > special_length) {
5482 return Top::Throw(Heap::illegal_argument_symbol());
5483 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005484 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005485 } else {
5486 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005487 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005488 // Get the position and check that it is also a smi.
5489 i++;
5490 if (i >= array_length) {
5491 return Top::Throw(Heap::illegal_argument_symbol());
5492 }
5493 Object* pos = fixed_array->get(i);
5494 if (!pos->IsSmi()) {
5495 return Top::Throw(Heap::illegal_argument_symbol());
5496 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005497 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 } else if (elt->IsString()) {
5499 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005500 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005501 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005502 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005503 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005504 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005505 } else {
5506 return Top::Throw(Heap::illegal_argument_symbol());
5507 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005508 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005509 Top::context()->mark_out_of_memory();
5510 return Failure::OutOfMemoryException();
5511 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005512 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005513 }
5514
5515 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005516 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005518 if (ascii) {
5519 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005520 if (object->IsFailure()) return object;
5521 SeqAsciiString* answer = SeqAsciiString::cast(object);
5522 StringBuilderConcatHelper(special,
5523 answer->GetChars(),
5524 fixed_array,
5525 array_length);
5526 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005527 } else {
5528 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005529 if (object->IsFailure()) return object;
5530 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5531 StringBuilderConcatHelper(special,
5532 answer->GetChars(),
5533 fixed_array,
5534 array_length);
5535 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005536 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005537}
5538
5539
5540static Object* Runtime_NumberOr(Arguments args) {
5541 NoHandleAllocation ha;
5542 ASSERT(args.length() == 2);
5543
5544 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5545 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5546 return Heap::NumberFromInt32(x | y);
5547}
5548
5549
5550static Object* Runtime_NumberAnd(Arguments args) {
5551 NoHandleAllocation ha;
5552 ASSERT(args.length() == 2);
5553
5554 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5555 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5556 return Heap::NumberFromInt32(x & y);
5557}
5558
5559
5560static Object* Runtime_NumberXor(Arguments args) {
5561 NoHandleAllocation ha;
5562 ASSERT(args.length() == 2);
5563
5564 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5565 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5566 return Heap::NumberFromInt32(x ^ y);
5567}
5568
5569
5570static Object* Runtime_NumberNot(Arguments args) {
5571 NoHandleAllocation ha;
5572 ASSERT(args.length() == 1);
5573
5574 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5575 return Heap::NumberFromInt32(~x);
5576}
5577
5578
5579static Object* Runtime_NumberShl(Arguments args) {
5580 NoHandleAllocation ha;
5581 ASSERT(args.length() == 2);
5582
5583 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5584 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5585 return Heap::NumberFromInt32(x << (y & 0x1f));
5586}
5587
5588
5589static Object* Runtime_NumberShr(Arguments args) {
5590 NoHandleAllocation ha;
5591 ASSERT(args.length() == 2);
5592
5593 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5594 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5595 return Heap::NumberFromUint32(x >> (y & 0x1f));
5596}
5597
5598
5599static Object* Runtime_NumberSar(Arguments args) {
5600 NoHandleAllocation ha;
5601 ASSERT(args.length() == 2);
5602
5603 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5604 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5605 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5606}
5607
5608
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005609static Object* Runtime_NumberEquals(Arguments args) {
5610 NoHandleAllocation ha;
5611 ASSERT(args.length() == 2);
5612
5613 CONVERT_DOUBLE_CHECKED(x, args[0]);
5614 CONVERT_DOUBLE_CHECKED(y, args[1]);
5615 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5616 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5617 if (x == y) return Smi::FromInt(EQUAL);
5618 Object* result;
5619 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5620 result = Smi::FromInt(EQUAL);
5621 } else {
5622 result = Smi::FromInt(NOT_EQUAL);
5623 }
5624 return result;
5625}
5626
5627
5628static Object* Runtime_StringEquals(Arguments args) {
5629 NoHandleAllocation ha;
5630 ASSERT(args.length() == 2);
5631
5632 CONVERT_CHECKED(String, x, args[0]);
5633 CONVERT_CHECKED(String, y, args[1]);
5634
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005635 bool not_equal = !x->Equals(y);
5636 // This is slightly convoluted because the value that signifies
5637 // equality is 0 and inequality is 1 so we have to negate the result
5638 // from String::Equals.
5639 ASSERT(not_equal == 0 || not_equal == 1);
5640 STATIC_CHECK(EQUAL == 0);
5641 STATIC_CHECK(NOT_EQUAL == 1);
5642 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005643}
5644
5645
5646static Object* Runtime_NumberCompare(Arguments args) {
5647 NoHandleAllocation ha;
5648 ASSERT(args.length() == 3);
5649
5650 CONVERT_DOUBLE_CHECKED(x, args[0]);
5651 CONVERT_DOUBLE_CHECKED(y, args[1]);
5652 if (isnan(x) || isnan(y)) return args[2];
5653 if (x == y) return Smi::FromInt(EQUAL);
5654 if (isless(x, y)) return Smi::FromInt(LESS);
5655 return Smi::FromInt(GREATER);
5656}
5657
5658
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005659// Compare two Smis as if they were converted to strings and then
5660// compared lexicographically.
5661static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5662 NoHandleAllocation ha;
5663 ASSERT(args.length() == 2);
5664
5665 // Arrays for the individual characters of the two Smis. Smis are
5666 // 31 bit integers and 10 decimal digits are therefore enough.
5667 static int x_elms[10];
5668 static int y_elms[10];
5669
5670 // Extract the integer values from the Smis.
5671 CONVERT_CHECKED(Smi, x, args[0]);
5672 CONVERT_CHECKED(Smi, y, args[1]);
5673 int x_value = x->value();
5674 int y_value = y->value();
5675
5676 // If the integers are equal so are the string representations.
5677 if (x_value == y_value) return Smi::FromInt(EQUAL);
5678
5679 // If one of the integers are zero the normal integer order is the
5680 // same as the lexicographic order of the string representations.
5681 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5682
ager@chromium.org32912102009-01-16 10:38:43 +00005683 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005684 // smallest because the char code of '-' is less than the char code
5685 // of any digit. Otherwise, we make both values positive.
5686 if (x_value < 0 || y_value < 0) {
5687 if (y_value >= 0) return Smi::FromInt(LESS);
5688 if (x_value >= 0) return Smi::FromInt(GREATER);
5689 x_value = -x_value;
5690 y_value = -y_value;
5691 }
5692
5693 // Convert the integers to arrays of their decimal digits.
5694 int x_index = 0;
5695 int y_index = 0;
5696 while (x_value > 0) {
5697 x_elms[x_index++] = x_value % 10;
5698 x_value /= 10;
5699 }
5700 while (y_value > 0) {
5701 y_elms[y_index++] = y_value % 10;
5702 y_value /= 10;
5703 }
5704
5705 // Loop through the arrays of decimal digits finding the first place
5706 // where they differ.
5707 while (--x_index >= 0 && --y_index >= 0) {
5708 int diff = x_elms[x_index] - y_elms[y_index];
5709 if (diff != 0) return Smi::FromInt(diff);
5710 }
5711
5712 // If one array is a suffix of the other array, the longest array is
5713 // the representation of the largest of the Smis in the
5714 // lexicographic ordering.
5715 return Smi::FromInt(x_index - y_index);
5716}
5717
5718
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005719static Object* StringInputBufferCompare(String* x, String* y) {
5720 static StringInputBuffer bufx;
5721 static StringInputBuffer bufy;
5722 bufx.Reset(x);
5723 bufy.Reset(y);
5724 while (bufx.has_more() && bufy.has_more()) {
5725 int d = bufx.GetNext() - bufy.GetNext();
5726 if (d < 0) return Smi::FromInt(LESS);
5727 else if (d > 0) return Smi::FromInt(GREATER);
5728 }
5729
5730 // x is (non-trivial) prefix of y:
5731 if (bufy.has_more()) return Smi::FromInt(LESS);
5732 // y is prefix of x:
5733 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5734}
5735
5736
5737static Object* FlatStringCompare(String* x, String* y) {
5738 ASSERT(x->IsFlat());
5739 ASSERT(y->IsFlat());
5740 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5741 int prefix_length = x->length();
5742 if (y->length() < prefix_length) {
5743 prefix_length = y->length();
5744 equal_prefix_result = Smi::FromInt(GREATER);
5745 } else if (y->length() > prefix_length) {
5746 equal_prefix_result = Smi::FromInt(LESS);
5747 }
5748 int r;
5749 if (x->IsAsciiRepresentation()) {
5750 Vector<const char> x_chars = x->ToAsciiVector();
5751 if (y->IsAsciiRepresentation()) {
5752 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005753 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005754 } else {
5755 Vector<const uc16> y_chars = y->ToUC16Vector();
5756 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5757 }
5758 } else {
5759 Vector<const uc16> x_chars = x->ToUC16Vector();
5760 if (y->IsAsciiRepresentation()) {
5761 Vector<const char> y_chars = y->ToAsciiVector();
5762 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5763 } else {
5764 Vector<const uc16> y_chars = y->ToUC16Vector();
5765 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5766 }
5767 }
5768 Object* result;
5769 if (r == 0) {
5770 result = equal_prefix_result;
5771 } else {
5772 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5773 }
5774 ASSERT(result == StringInputBufferCompare(x, y));
5775 return result;
5776}
5777
5778
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005779static Object* Runtime_StringCompare(Arguments args) {
5780 NoHandleAllocation ha;
5781 ASSERT(args.length() == 2);
5782
5783 CONVERT_CHECKED(String, x, args[0]);
5784 CONVERT_CHECKED(String, y, args[1]);
5785
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005786 Counters::string_compare_runtime.Increment();
5787
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005788 // A few fast case tests before we flatten.
5789 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005790 if (y->length() == 0) {
5791 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005792 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005793 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005794 return Smi::FromInt(LESS);
5795 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005796
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005797 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005798 if (d < 0) return Smi::FromInt(LESS);
5799 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005800
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005801 Object* obj = Heap::PrepareForCompare(x);
5802 if (obj->IsFailure()) return obj;
5803 obj = Heap::PrepareForCompare(y);
5804 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005805
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005806 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5807 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808}
5809
5810
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005811static Object* Runtime_Math_acos(Arguments args) {
5812 NoHandleAllocation ha;
5813 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005814 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005815
5816 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005817 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005818}
5819
5820
5821static Object* Runtime_Math_asin(Arguments args) {
5822 NoHandleAllocation ha;
5823 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005824 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005825
5826 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005827 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005828}
5829
5830
5831static Object* Runtime_Math_atan(Arguments args) {
5832 NoHandleAllocation ha;
5833 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005834 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005835
5836 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005837 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005838}
5839
5840
5841static Object* Runtime_Math_atan2(Arguments args) {
5842 NoHandleAllocation ha;
5843 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005844 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005845
5846 CONVERT_DOUBLE_CHECKED(x, args[0]);
5847 CONVERT_DOUBLE_CHECKED(y, args[1]);
5848 double result;
5849 if (isinf(x) && isinf(y)) {
5850 // Make sure that the result in case of two infinite arguments
5851 // is a multiple of Pi / 4. The sign of the result is determined
5852 // by the first argument (x) and the sign of the second argument
5853 // determines the multiplier: one or three.
5854 static double kPiDividedBy4 = 0.78539816339744830962;
5855 int multiplier = (x < 0) ? -1 : 1;
5856 if (y < 0) multiplier *= 3;
5857 result = multiplier * kPiDividedBy4;
5858 } else {
5859 result = atan2(x, y);
5860 }
5861 return Heap::AllocateHeapNumber(result);
5862}
5863
5864
5865static Object* Runtime_Math_ceil(Arguments args) {
5866 NoHandleAllocation ha;
5867 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005868 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005869
5870 CONVERT_DOUBLE_CHECKED(x, args[0]);
5871 return Heap::NumberFromDouble(ceiling(x));
5872}
5873
5874
5875static Object* Runtime_Math_cos(Arguments args) {
5876 NoHandleAllocation ha;
5877 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005878 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005879
5880 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005881 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005882}
5883
5884
5885static Object* Runtime_Math_exp(Arguments args) {
5886 NoHandleAllocation ha;
5887 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005888 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005889
5890 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005891 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005892}
5893
5894
5895static Object* Runtime_Math_floor(Arguments args) {
5896 NoHandleAllocation ha;
5897 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005898 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005899
5900 CONVERT_DOUBLE_CHECKED(x, args[0]);
5901 return Heap::NumberFromDouble(floor(x));
5902}
5903
5904
5905static Object* Runtime_Math_log(Arguments args) {
5906 NoHandleAllocation ha;
5907 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005908 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005909
5910 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005911 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005912}
5913
5914
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005915// Helper function to compute x^y, where y is known to be an
5916// integer. Uses binary decomposition to limit the number of
5917// multiplications; see the discussion in "Hacker's Delight" by Henry
5918// S. Warren, Jr., figure 11-6, page 213.
5919static double powi(double x, int y) {
5920 ASSERT(y != kMinInt);
5921 unsigned n = (y < 0) ? -y : y;
5922 double m = x;
5923 double p = 1;
5924 while (true) {
5925 if ((n & 1) != 0) p *= m;
5926 n >>= 1;
5927 if (n == 0) {
5928 if (y < 0) {
5929 // Unfortunately, we have to be careful when p has reached
5930 // infinity in the computation, because sometimes the higher
5931 // internal precision in the pow() implementation would have
5932 // given us a finite p. This happens very rarely.
5933 double result = 1.0 / p;
5934 return (result == 0 && isinf(p))
5935 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
5936 : result;
5937 } else {
5938 return p;
5939 }
5940 }
5941 m *= m;
5942 }
5943}
5944
5945
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005946static Object* Runtime_Math_pow(Arguments args) {
5947 NoHandleAllocation ha;
5948 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005949 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005950
5951 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005952
5953 // If the second argument is a smi, it is much faster to call the
5954 // custom powi() function than the generic pow().
5955 if (args[1]->IsSmi()) {
5956 int y = Smi::cast(args[1])->value();
5957 return Heap::AllocateHeapNumber(powi(x, y));
5958 }
5959
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005960 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005961
5962 if (!isinf(x)) {
5963 if (y == 0.5) {
5964 // It's not uncommon to use Math.pow(x, 0.5) to compute the
5965 // square root of a number. To speed up such computations, we
5966 // explictly check for this case and use the sqrt() function
5967 // which is faster than pow().
5968 return Heap::AllocateHeapNumber(sqrt(x));
5969 } else if (y == -0.5) {
5970 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
5971 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
5972 }
5973 }
5974
5975 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005976 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005977 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5978 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005979 } else {
5980 return Heap::AllocateHeapNumber(pow(x, y));
5981 }
5982}
5983
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005984// Fast version of Math.pow if we know that y is not an integer and
5985// y is not -0.5 or 0.5. Used as slowcase from codegen.
5986static Object* Runtime_Math_pow_cfunction(Arguments args) {
5987 NoHandleAllocation ha;
5988 ASSERT(args.length() == 2);
5989 CONVERT_DOUBLE_CHECKED(x, args[0]);
5990 CONVERT_DOUBLE_CHECKED(y, args[1]);
5991 if (y == 0) {
5992 return Smi::FromInt(1);
5993 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5994 return Heap::nan_value();
5995 } else {
5996 return Heap::AllocateHeapNumber(pow(x, y));
5997 }
5998}
5999
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006000
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006001static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006002 NoHandleAllocation ha;
6003 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006004 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006005
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006006 if (!args[0]->IsHeapNumber()) {
6007 // Must be smi. Return the argument unchanged for all the other types
6008 // to make fuzz-natives test happy.
6009 return args[0];
6010 }
6011
6012 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6013
6014 double value = number->value();
6015 int exponent = number->get_exponent();
6016 int sign = number->get_sign();
6017
6018 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6019 // should be rounded to 2^30, which is not smi.
6020 if (!sign && exponent <= kSmiValueSize - 3) {
6021 return Smi::FromInt(static_cast<int>(value + 0.5));
6022 }
6023
6024 // If the magnitude is big enough, there's no place for fraction part. If we
6025 // try to add 0.5 to this number, 1.0 will be added instead.
6026 if (exponent >= 52) {
6027 return number;
6028 }
6029
6030 if (sign && value >= -0.5) return Heap::minus_zero_value();
6031
6032 return Heap::NumberFromDouble(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006033}
6034
6035
6036static Object* Runtime_Math_sin(Arguments args) {
6037 NoHandleAllocation ha;
6038 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006039 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006040
6041 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006042 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006043}
6044
6045
6046static Object* Runtime_Math_sqrt(Arguments args) {
6047 NoHandleAllocation ha;
6048 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006049 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006050
6051 CONVERT_DOUBLE_CHECKED(x, args[0]);
6052 return Heap::AllocateHeapNumber(sqrt(x));
6053}
6054
6055
6056static Object* Runtime_Math_tan(Arguments args) {
6057 NoHandleAllocation ha;
6058 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006059 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006060
6061 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006062 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006063}
6064
6065
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006066static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006067 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6068 181, 212, 243, 273, 304, 334};
6069 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6070 182, 213, 244, 274, 305, 335};
6071
6072 year += month / 12;
6073 month %= 12;
6074 if (month < 0) {
6075 year--;
6076 month += 12;
6077 }
6078
6079 ASSERT(month >= 0);
6080 ASSERT(month < 12);
6081
6082 // year_delta is an arbitrary number such that:
6083 // a) year_delta = -1 (mod 400)
6084 // b) year + year_delta > 0 for years in the range defined by
6085 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6086 // Jan 1 1970. This is required so that we don't run into integer
6087 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006088 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006089 // operations.
6090 static const int year_delta = 399999;
6091 static const int base_day = 365 * (1970 + year_delta) +
6092 (1970 + year_delta) / 4 -
6093 (1970 + year_delta) / 100 +
6094 (1970 + year_delta) / 400;
6095
6096 int year1 = year + year_delta;
6097 int day_from_year = 365 * year1 +
6098 year1 / 4 -
6099 year1 / 100 +
6100 year1 / 400 -
6101 base_day;
6102
6103 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006104 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006105 }
6106
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006107 return day_from_year + day_from_month_leap[month] + day - 1;
6108}
6109
6110
6111static Object* Runtime_DateMakeDay(Arguments args) {
6112 NoHandleAllocation ha;
6113 ASSERT(args.length() == 3);
6114
6115 CONVERT_SMI_CHECKED(year, args[0]);
6116 CONVERT_SMI_CHECKED(month, args[1]);
6117 CONVERT_SMI_CHECKED(date, args[2]);
6118
6119 return Smi::FromInt(MakeDay(year, month, date));
6120}
6121
6122
6123static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6124static const int kDaysIn4Years = 4 * 365 + 1;
6125static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6126static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6127static const int kDays1970to2000 = 30 * 365 + 7;
6128static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6129 kDays1970to2000;
6130static const int kYearsOffset = 400000;
6131
6132static const char kDayInYear[] = {
6133 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6134 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6135 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6136 22, 23, 24, 25, 26, 27, 28,
6137 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6138 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6139 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6140 22, 23, 24, 25, 26, 27, 28, 29, 30,
6141 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6142 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6143 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6144 22, 23, 24, 25, 26, 27, 28, 29, 30,
6145 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6146 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6147 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6148 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6149 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6150 22, 23, 24, 25, 26, 27, 28, 29, 30,
6151 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6152 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6153 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6154 22, 23, 24, 25, 26, 27, 28, 29, 30,
6155 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6156 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6157
6158 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6159 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6160 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6161 22, 23, 24, 25, 26, 27, 28,
6162 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6163 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6164 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6165 22, 23, 24, 25, 26, 27, 28, 29, 30,
6166 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6167 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6168 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6169 22, 23, 24, 25, 26, 27, 28, 29, 30,
6170 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6171 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6172 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6173 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6174 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6175 22, 23, 24, 25, 26, 27, 28, 29, 30,
6176 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6177 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6178 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6179 22, 23, 24, 25, 26, 27, 28, 29, 30,
6180 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6181 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6182
6183 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6184 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6185 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6186 22, 23, 24, 25, 26, 27, 28, 29,
6187 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6188 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6189 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6190 22, 23, 24, 25, 26, 27, 28, 29, 30,
6191 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6192 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6193 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6194 22, 23, 24, 25, 26, 27, 28, 29, 30,
6195 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6196 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6197 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6198 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6199 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6200 22, 23, 24, 25, 26, 27, 28, 29, 30,
6201 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6202 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6203 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6204 22, 23, 24, 25, 26, 27, 28, 29, 30,
6205 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6206 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6207
6208 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6209 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6210 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6211 22, 23, 24, 25, 26, 27, 28,
6212 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6213 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6214 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6215 22, 23, 24, 25, 26, 27, 28, 29, 30,
6216 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6217 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6218 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6219 22, 23, 24, 25, 26, 27, 28, 29, 30,
6220 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6221 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6222 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6223 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6224 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6225 22, 23, 24, 25, 26, 27, 28, 29, 30,
6226 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6227 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6228 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6229 22, 23, 24, 25, 26, 27, 28, 29, 30,
6230 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6231 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6232
6233static const char kMonthInYear[] = {
6234 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6235 0, 0, 0, 0, 0, 0,
6236 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6237 1, 1, 1,
6238 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6239 2, 2, 2, 2, 2, 2,
6240 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6241 3, 3, 3, 3, 3,
6242 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6243 4, 4, 4, 4, 4, 4,
6244 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6245 5, 5, 5, 5, 5,
6246 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6247 6, 6, 6, 6, 6, 6,
6248 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6249 7, 7, 7, 7, 7, 7,
6250 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6251 8, 8, 8, 8, 8,
6252 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6253 9, 9, 9, 9, 9, 9,
6254 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6255 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6256 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6257 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6258
6259 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6260 0, 0, 0, 0, 0, 0,
6261 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6262 1, 1, 1,
6263 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6264 2, 2, 2, 2, 2, 2,
6265 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6266 3, 3, 3, 3, 3,
6267 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6268 4, 4, 4, 4, 4, 4,
6269 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6270 5, 5, 5, 5, 5,
6271 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6272 6, 6, 6, 6, 6, 6,
6273 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6274 7, 7, 7, 7, 7, 7,
6275 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6276 8, 8, 8, 8, 8,
6277 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6278 9, 9, 9, 9, 9, 9,
6279 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6280 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6281 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6282 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6283
6284 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6285 0, 0, 0, 0, 0, 0,
6286 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6287 1, 1, 1, 1,
6288 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6289 2, 2, 2, 2, 2, 2,
6290 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6291 3, 3, 3, 3, 3,
6292 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6293 4, 4, 4, 4, 4, 4,
6294 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6295 5, 5, 5, 5, 5,
6296 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6297 6, 6, 6, 6, 6, 6,
6298 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6299 7, 7, 7, 7, 7, 7,
6300 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6301 8, 8, 8, 8, 8,
6302 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6303 9, 9, 9, 9, 9, 9,
6304 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6305 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6306 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6307 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6308
6309 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6310 0, 0, 0, 0, 0, 0,
6311 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6312 1, 1, 1,
6313 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6314 2, 2, 2, 2, 2, 2,
6315 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6316 3, 3, 3, 3, 3,
6317 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6318 4, 4, 4, 4, 4, 4,
6319 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6320 5, 5, 5, 5, 5,
6321 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6322 6, 6, 6, 6, 6, 6,
6323 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6324 7, 7, 7, 7, 7, 7,
6325 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6326 8, 8, 8, 8, 8,
6327 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6328 9, 9, 9, 9, 9, 9,
6329 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6330 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6331 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6332 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6333
6334
6335// This function works for dates from 1970 to 2099.
6336static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006337 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006338#ifdef DEBUG
6339 int save_date = date; // Need this for ASSERT in the end.
6340#endif
6341
6342 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6343 date %= kDaysIn4Years;
6344
6345 month = kMonthInYear[date];
6346 day = kDayInYear[date];
6347
6348 ASSERT(MakeDay(year, month, day) == save_date);
6349}
6350
6351
6352static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006353 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006354#ifdef DEBUG
6355 int save_date = date; // Need this for ASSERT in the end.
6356#endif
6357
6358 date += kDaysOffset;
6359 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6360 date %= kDaysIn400Years;
6361
6362 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6363
6364 date--;
6365 int yd1 = date / kDaysIn100Years;
6366 date %= kDaysIn100Years;
6367 year += 100 * yd1;
6368
6369 date++;
6370 int yd2 = date / kDaysIn4Years;
6371 date %= kDaysIn4Years;
6372 year += 4 * yd2;
6373
6374 date--;
6375 int yd3 = date / 365;
6376 date %= 365;
6377 year += yd3;
6378
6379 bool is_leap = (!yd1 || yd2) && !yd3;
6380
6381 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006382 ASSERT(is_leap || (date >= 0));
6383 ASSERT((date < 365) || (is_leap && (date < 366)));
6384 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6385 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6386 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006387
6388 if (is_leap) {
6389 day = kDayInYear[2*365 + 1 + date];
6390 month = kMonthInYear[2*365 + 1 + date];
6391 } else {
6392 day = kDayInYear[date];
6393 month = kMonthInYear[date];
6394 }
6395
6396 ASSERT(MakeDay(year, month, day) == save_date);
6397}
6398
6399
6400static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006401 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006402 if (date >= 0 && date < 32 * kDaysIn4Years) {
6403 DateYMDFromTimeAfter1970(date, year, month, day);
6404 } else {
6405 DateYMDFromTimeSlow(date, year, month, day);
6406 }
6407}
6408
6409
6410static Object* Runtime_DateYMDFromTime(Arguments args) {
6411 NoHandleAllocation ha;
6412 ASSERT(args.length() == 2);
6413
6414 CONVERT_DOUBLE_CHECKED(t, args[0]);
6415 CONVERT_CHECKED(JSArray, res_array, args[1]);
6416
6417 int year, month, day;
6418 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6419
6420 res_array->SetElement(0, Smi::FromInt(year));
6421 res_array->SetElement(1, Smi::FromInt(month));
6422 res_array->SetElement(2, Smi::FromInt(day));
6423
6424 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006425}
6426
6427
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006428static Object* Runtime_NewArgumentsFast(Arguments args) {
6429 NoHandleAllocation ha;
6430 ASSERT(args.length() == 3);
6431
6432 JSFunction* callee = JSFunction::cast(args[0]);
6433 Object** parameters = reinterpret_cast<Object**>(args[1]);
6434 const int length = Smi::cast(args[2])->value();
6435
6436 Object* result = Heap::AllocateArgumentsObject(callee, length);
6437 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006438 // Allocate the elements if needed.
6439 if (length > 0) {
6440 // Allocate the fixed array.
6441 Object* obj = Heap::AllocateRawFixedArray(length);
6442 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006443
6444 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006445 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
6446 FixedArray* array = FixedArray::cast(obj);
6447 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006448
6449 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006450 for (int i = 0; i < length; i++) {
6451 array->set(i, *--parameters, mode);
6452 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006453 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006454 }
6455 return result;
6456}
6457
6458
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006459static Object* Runtime_NewClosure(Arguments args) {
6460 HandleScope scope;
6461 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006462 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006463 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006464
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006465 PretenureFlag pretenure = (context->global_context() == *context)
6466 ? TENURED // Allocate global closures in old space.
6467 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006468 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006469 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006470 return *result;
6471}
6472
6473
ager@chromium.org5c838252010-02-19 08:53:10 +00006474static Code* ComputeConstructStub(Handle<JSFunction> function) {
6475 Handle<Object> prototype = Factory::null_value();
6476 if (function->has_instance_prototype()) {
6477 prototype = Handle<Object>(function->instance_prototype());
6478 }
6479 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006480 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006481 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006482 if (code->IsFailure()) {
6483 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6484 }
6485 return Code::cast(code);
6486 }
6487
ager@chromium.org5c838252010-02-19 08:53:10 +00006488 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006489}
6490
6491
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006492static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006493 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006494 ASSERT(args.length() == 1);
6495
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006496 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006497
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006498 // If the constructor isn't a proper function we throw a type error.
6499 if (!constructor->IsJSFunction()) {
6500 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6501 Handle<Object> type_error =
6502 Factory::NewTypeError("not_constructor", arguments);
6503 return Top::Throw(*type_error);
6504 }
6505
6506 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006507#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006508 // Handle stepping into constructors if step into is active.
6509 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006510 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006511 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006512#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006513
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006514 if (function->has_initial_map()) {
6515 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006516 // The 'Function' function ignores the receiver object when
6517 // called using 'new' and creates a new JSFunction object that
6518 // is returned. The receiver object is only used for error
6519 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006520 // JSFunction. Factory::NewJSObject() should not be used to
6521 // allocate JSFunctions since it does not properly initialize
6522 // the shared part of the function. Since the receiver is
6523 // ignored anyway, we use the global object as the receiver
6524 // instead of a new JSFunction object. This way, errors are
6525 // reported the same way whether or not 'Function' is called
6526 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006527 return Top::context()->global();
6528 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006529 }
6530
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006531 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006532 Handle<SharedFunctionInfo> shared(function->shared());
6533 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006534
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006535 bool first_allocation = !function->has_initial_map();
6536 Handle<JSObject> result = Factory::NewJSObject(function);
6537 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006538 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006539 ComputeConstructStub(Handle<JSFunction>(function)));
6540 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006541 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006542
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006543 Counters::constructed_objects.Increment();
6544 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006545
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006546 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006547}
6548
6549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006550static Object* Runtime_LazyCompile(Arguments args) {
6551 HandleScope scope;
6552 ASSERT(args.length() == 1);
6553
6554 Handle<JSFunction> function = args.at<JSFunction>(0);
6555#ifdef DEBUG
6556 if (FLAG_trace_lazy) {
6557 PrintF("[lazy: ");
6558 function->shared()->name()->Print();
6559 PrintF("]\n");
6560 }
6561#endif
6562
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006563 // Compile the target function. Here we compile using CompileLazyInLoop in
6564 // order to get the optimized version. This helps code like delta-blue
6565 // that calls performance-critical routines through constructors. A
6566 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6567 // direct call. Since the in-loop tracking takes place through CallICs
6568 // this means that things called through constructors are never known to
6569 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006570 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006571 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006572 return Failure::Exception();
6573 }
6574
6575 return function->code();
6576}
6577
6578
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006579static Object* Runtime_GetFunctionDelegate(Arguments args) {
6580 HandleScope scope;
6581 ASSERT(args.length() == 1);
6582 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6583 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6584}
6585
6586
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006587static Object* Runtime_GetConstructorDelegate(Arguments args) {
6588 HandleScope scope;
6589 ASSERT(args.length() == 1);
6590 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6591 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6592}
6593
6594
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006595static Object* Runtime_NewContext(Arguments args) {
6596 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006597 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006598
kasper.lund7276f142008-07-30 08:49:36 +00006599 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006600 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6601 Object* result = Heap::AllocateFunctionContext(length, function);
6602 if (result->IsFailure()) return result;
6603
6604 Top::set_context(Context::cast(result));
6605
kasper.lund7276f142008-07-30 08:49:36 +00006606 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006607}
6608
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006609static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006610 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006611 Object* js_object = object;
6612 if (!js_object->IsJSObject()) {
6613 js_object = js_object->ToObject();
6614 if (js_object->IsFailure()) {
6615 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006616 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006617 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006618 Handle<Object> result =
6619 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6620 return Top::Throw(*result);
6621 }
6622 }
6623
6624 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006625 Heap::AllocateWithContext(Top::context(),
6626 JSObject::cast(js_object),
6627 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006628 if (result->IsFailure()) return result;
6629
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006630 Context* context = Context::cast(result);
6631 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006632
kasper.lund7276f142008-07-30 08:49:36 +00006633 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006634}
6635
6636
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006637static Object* Runtime_PushContext(Arguments args) {
6638 NoHandleAllocation ha;
6639 ASSERT(args.length() == 1);
6640 return PushContextHelper(args[0], false);
6641}
6642
6643
6644static Object* Runtime_PushCatchContext(Arguments args) {
6645 NoHandleAllocation ha;
6646 ASSERT(args.length() == 1);
6647 return PushContextHelper(args[0], true);
6648}
6649
6650
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006651static Object* Runtime_LookupContext(Arguments args) {
6652 HandleScope scope;
6653 ASSERT(args.length() == 2);
6654
6655 CONVERT_ARG_CHECKED(Context, context, 0);
6656 CONVERT_ARG_CHECKED(String, name, 1);
6657
6658 int index;
6659 PropertyAttributes attributes;
6660 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006661 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006662 context->Lookup(name, flags, &index, &attributes);
6663
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006664 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006665 ASSERT(holder->IsJSObject());
6666 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006667 }
6668
6669 // No intermediate context found. Use global object by default.
6670 return Top::context()->global();
6671}
6672
6673
ager@chromium.orga1645e22009-09-09 19:27:10 +00006674// A mechanism to return a pair of Object pointers in registers (if possible).
6675// How this is achieved is calling convention-dependent.
6676// All currently supported x86 compiles uses calling conventions that are cdecl
6677// variants where a 64-bit value is returned in two 32-bit registers
6678// (edx:eax on ia32, r1:r0 on ARM).
6679// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6680// In Win64 calling convention, a struct of two pointers is returned in memory,
6681// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006682#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006683struct ObjectPair {
6684 Object* x;
6685 Object* y;
6686};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006687
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006688static inline ObjectPair MakePair(Object* x, Object* y) {
6689 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006690 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6691 // In Win64 they are assigned to a hidden first argument.
6692 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006693}
6694#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006695typedef uint64_t ObjectPair;
6696static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006697 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006698 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006699}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006700#endif
6701
6702
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006703static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006704 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6705 USE(attributes);
6706 return x->IsTheHole() ? Heap::undefined_value() : x;
6707}
6708
6709
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006710static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6711 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006712 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006713 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006714 JSFunction* context_extension_function =
6715 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006716 // If the holder isn't a context extension object, we just return it
6717 // as the receiver. This allows arguments objects to be used as
6718 // receivers, but only if they are put in the context scope chain
6719 // explicitly via a with-statement.
6720 Object* constructor = holder->map()->constructor();
6721 if (constructor != context_extension_function) return holder;
6722 // Fall back to using the global object as the receiver if the
6723 // property turns out to be a local variable allocated in a context
6724 // extension object - introduced via eval.
6725 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006726}
6727
6728
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006729static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006730 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006731 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006732
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006733 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006734 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006735 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006736 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006737 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006738
6739 int index;
6740 PropertyAttributes attributes;
6741 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006742 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006743 context->Lookup(name, flags, &index, &attributes);
6744
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006745 // If the index is non-negative, the slot has been found in a local
6746 // variable or a parameter. Read it from the context object or the
6747 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006748 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006749 // If the "property" we were looking for is a local variable or an
6750 // argument in a context, the receiver is the global object; see
6751 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6752 JSObject* receiver = Top::context()->global()->global_receiver();
6753 Object* value = (holder->IsContext())
6754 ? Context::cast(*holder)->get(index)
6755 : JSObject::cast(*holder)->GetElement(index);
6756 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006757 }
6758
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006759 // If the holder is found, we read the property from it.
6760 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006761 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006762 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006763 JSObject* receiver;
6764 if (object->IsGlobalObject()) {
6765 receiver = GlobalObject::cast(object)->global_receiver();
6766 } else if (context->is_exception_holder(*holder)) {
6767 receiver = Top::context()->global()->global_receiver();
6768 } else {
6769 receiver = ComputeReceiverForNonGlobal(object);
6770 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006771 // No need to unhole the value here. This is taken care of by the
6772 // GetProperty function.
6773 Object* value = object->GetProperty(*name);
6774 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006775 }
6776
6777 if (throw_error) {
6778 // The property doesn't exist - throw exception.
6779 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006780 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006781 return MakePair(Top::Throw(*reference_error), NULL);
6782 } else {
6783 // The property doesn't exist - return undefined
6784 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6785 }
6786}
6787
6788
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006789static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006790 return LoadContextSlotHelper(args, true);
6791}
6792
6793
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006794static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006795 return LoadContextSlotHelper(args, false);
6796}
6797
6798
6799static Object* Runtime_StoreContextSlot(Arguments args) {
6800 HandleScope scope;
6801 ASSERT(args.length() == 3);
6802
6803 Handle<Object> value(args[0]);
6804 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006805 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006806
6807 int index;
6808 PropertyAttributes attributes;
6809 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006810 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006811 context->Lookup(name, flags, &index, &attributes);
6812
6813 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006814 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006815 // Ignore if read_only variable.
6816 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006817 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006818 }
6819 } else {
6820 ASSERT((attributes & READ_ONLY) == 0);
6821 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006822 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006823 USE(result);
6824 ASSERT(!result->IsFailure());
6825 }
6826 return *value;
6827 }
6828
6829 // Slow case: The property is not in a FixedArray context.
6830 // It is either in an JSObject extension context or it was not found.
6831 Handle<JSObject> context_ext;
6832
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006833 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006834 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006835 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006836 } else {
6837 // The property was not found. It needs to be stored in the global context.
6838 ASSERT(attributes == ABSENT);
6839 attributes = NONE;
6840 context_ext = Handle<JSObject>(Top::context()->global());
6841 }
6842
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006843 // Set the property, but ignore if read_only variable on the context
6844 // extension object itself.
6845 if ((attributes & READ_ONLY) == 0 ||
6846 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006847 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6848 if (set.is_null()) {
6849 // Failure::Exception is converted to a null handle in the
6850 // handle-based methods such as SetProperty. We therefore need
6851 // to convert null handles back to exceptions.
6852 ASSERT(Top::has_pending_exception());
6853 return Failure::Exception();
6854 }
6855 }
6856 return *value;
6857}
6858
6859
6860static Object* Runtime_Throw(Arguments args) {
6861 HandleScope scope;
6862 ASSERT(args.length() == 1);
6863
6864 return Top::Throw(args[0]);
6865}
6866
6867
6868static Object* Runtime_ReThrow(Arguments args) {
6869 HandleScope scope;
6870 ASSERT(args.length() == 1);
6871
6872 return Top::ReThrow(args[0]);
6873}
6874
6875
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006876static Object* Runtime_PromoteScheduledException(Arguments args) {
6877 ASSERT_EQ(0, args.length());
6878 return Top::PromoteScheduledException();
6879}
6880
6881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006882static Object* Runtime_ThrowReferenceError(Arguments args) {
6883 HandleScope scope;
6884 ASSERT(args.length() == 1);
6885
6886 Handle<Object> name(args[0]);
6887 Handle<Object> reference_error =
6888 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6889 return Top::Throw(*reference_error);
6890}
6891
6892
6893static Object* Runtime_StackOverflow(Arguments args) {
6894 NoHandleAllocation na;
6895 return Top::StackOverflow();
6896}
6897
6898
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006899static Object* Runtime_StackGuard(Arguments args) {
6900 ASSERT(args.length() == 1);
6901
6902 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006903 if (StackGuard::IsStackOverflow()) {
6904 return Runtime_StackOverflow(args);
6905 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006906
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006907 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006908}
6909
6910
6911// NOTE: These PrintXXX functions are defined for all builds (not just
6912// DEBUG builds) because we may want to be able to trace function
6913// calls in all modes.
6914static void PrintString(String* str) {
6915 // not uncommon to have empty strings
6916 if (str->length() > 0) {
6917 SmartPointer<char> s =
6918 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
6919 PrintF("%s", *s);
6920 }
6921}
6922
6923
6924static void PrintObject(Object* obj) {
6925 if (obj->IsSmi()) {
6926 PrintF("%d", Smi::cast(obj)->value());
6927 } else if (obj->IsString() || obj->IsSymbol()) {
6928 PrintString(String::cast(obj));
6929 } else if (obj->IsNumber()) {
6930 PrintF("%g", obj->Number());
6931 } else if (obj->IsFailure()) {
6932 PrintF("<failure>");
6933 } else if (obj->IsUndefined()) {
6934 PrintF("<undefined>");
6935 } else if (obj->IsNull()) {
6936 PrintF("<null>");
6937 } else if (obj->IsTrue()) {
6938 PrintF("<true>");
6939 } else if (obj->IsFalse()) {
6940 PrintF("<false>");
6941 } else {
6942 PrintF("%p", obj);
6943 }
6944}
6945
6946
6947static int StackSize() {
6948 int n = 0;
6949 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
6950 return n;
6951}
6952
6953
6954static void PrintTransition(Object* result) {
6955 // indentation
6956 { const int nmax = 80;
6957 int n = StackSize();
6958 if (n <= nmax)
6959 PrintF("%4d:%*s", n, n, "");
6960 else
6961 PrintF("%4d:%*s", n, nmax, "...");
6962 }
6963
6964 if (result == NULL) {
6965 // constructor calls
6966 JavaScriptFrameIterator it;
6967 JavaScriptFrame* frame = it.frame();
6968 if (frame->IsConstructor()) PrintF("new ");
6969 // function name
6970 Object* fun = frame->function();
6971 if (fun->IsJSFunction()) {
6972 PrintObject(JSFunction::cast(fun)->shared()->name());
6973 } else {
6974 PrintObject(fun);
6975 }
6976 // function arguments
6977 // (we are intentionally only printing the actually
6978 // supplied parameters, not all parameters required)
6979 PrintF("(this=");
6980 PrintObject(frame->receiver());
6981 const int length = frame->GetProvidedParametersCount();
6982 for (int i = 0; i < length; i++) {
6983 PrintF(", ");
6984 PrintObject(frame->GetParameter(i));
6985 }
6986 PrintF(") {\n");
6987
6988 } else {
6989 // function result
6990 PrintF("} -> ");
6991 PrintObject(result);
6992 PrintF("\n");
6993 }
6994}
6995
6996
6997static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006998 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006999 NoHandleAllocation ha;
7000 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007001 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007002}
7003
7004
7005static Object* Runtime_TraceExit(Arguments args) {
7006 NoHandleAllocation ha;
7007 PrintTransition(args[0]);
7008 return args[0]; // return TOS
7009}
7010
7011
7012static Object* Runtime_DebugPrint(Arguments args) {
7013 NoHandleAllocation ha;
7014 ASSERT(args.length() == 1);
7015
7016#ifdef DEBUG
7017 if (args[0]->IsString()) {
7018 // If we have a string, assume it's a code "marker"
7019 // and print some interesting cpu debugging info.
7020 JavaScriptFrameIterator it;
7021 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007022 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7023 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007024 } else {
7025 PrintF("DebugPrint: ");
7026 }
7027 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007028 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007029 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007030 HeapObject::cast(args[0])->map()->Print();
7031 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007032#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007033 // ShortPrint is available in release mode. Print is not.
7034 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007035#endif
7036 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007037 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007038
7039 return args[0]; // return TOS
7040}
7041
7042
7043static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007044 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007045 NoHandleAllocation ha;
7046 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007047 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007048}
7049
7050
mads.s.ager31e71382008-08-13 09:32:07 +00007051static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007052 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007053 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054
7055 // According to ECMA-262, section 15.9.1, page 117, the precision of
7056 // the number in a Date object representing a particular instant in
7057 // time is milliseconds. Therefore, we floor the result of getting
7058 // the OS time.
7059 double millis = floor(OS::TimeCurrentMillis());
7060 return Heap::NumberFromDouble(millis);
7061}
7062
7063
7064static Object* Runtime_DateParseString(Arguments args) {
7065 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007066 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007067
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007068 CONVERT_ARG_CHECKED(String, str, 0);
7069 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007070
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007071 CONVERT_ARG_CHECKED(JSArray, output, 1);
7072 RUNTIME_ASSERT(output->HasFastElements());
7073
7074 AssertNoAllocation no_allocation;
7075
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007076 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007077 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7078 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007079 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007080 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007081 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007082 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007083 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7084 }
7085
7086 if (result) {
7087 return *output;
7088 } else {
7089 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007090 }
7091}
7092
7093
7094static Object* Runtime_DateLocalTimezone(Arguments args) {
7095 NoHandleAllocation ha;
7096 ASSERT(args.length() == 1);
7097
7098 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007099 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7101}
7102
7103
7104static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7105 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007106 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007107
7108 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7109}
7110
7111
7112static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7113 NoHandleAllocation ha;
7114 ASSERT(args.length() == 1);
7115
7116 CONVERT_DOUBLE_CHECKED(x, args[0]);
7117 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7118}
7119
7120
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007121static Object* Runtime_GlobalReceiver(Arguments args) {
7122 ASSERT(args.length() == 1);
7123 Object* global = args[0];
7124 if (!global->IsJSGlobalObject()) return Heap::null_value();
7125 return JSGlobalObject::cast(global)->global_receiver();
7126}
7127
7128
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007129static Object* Runtime_CompileString(Arguments args) {
7130 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007131 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007132 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007133 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007134
ager@chromium.org381abbb2009-02-25 13:23:22 +00007135 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007136 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007137 Compiler::ValidationState validate = (is_json->IsTrue())
7138 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007139 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7140 context,
7141 true,
7142 validate);
7143 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007144 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007145 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007146 return *fun;
7147}
7148
7149
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007150static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7151 ASSERT(args.length() == 3);
7152 if (!args[0]->IsJSFunction()) {
7153 return MakePair(Top::ThrowIllegalOperation(), NULL);
7154 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007155
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007156 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007157 Handle<JSFunction> callee = args.at<JSFunction>(0);
7158 Handle<Object> receiver; // Will be overwritten.
7159
7160 // Compute the calling context.
7161 Handle<Context> context = Handle<Context>(Top::context());
7162#ifdef DEBUG
7163 // Make sure Top::context() agrees with the old code that traversed
7164 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007165 StackFrameLocator locator;
7166 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007167 ASSERT(Context::cast(frame->context()) == *context);
7168#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007169
7170 // Find where the 'eval' symbol is bound. It is unaliased only if
7171 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007172 int index = -1;
7173 PropertyAttributes attributes = ABSENT;
7174 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007175 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7176 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007177 // Stop search when eval is found or when the global context is
7178 // reached.
7179 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007180 if (context->is_function_context()) {
7181 context = Handle<Context>(Context::cast(context->closure()->context()));
7182 } else {
7183 context = Handle<Context>(context->previous());
7184 }
7185 }
7186
iposva@chromium.org245aa852009-02-10 00:49:54 +00007187 // If eval could not be resolved, it has been deleted and we need to
7188 // throw a reference error.
7189 if (attributes == ABSENT) {
7190 Handle<Object> name = Factory::eval_symbol();
7191 Handle<Object> reference_error =
7192 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007193 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007194 }
7195
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007196 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007197 // 'eval' is not bound in the global context. Just call the function
7198 // with the given arguments. This is not necessarily the global eval.
7199 if (receiver->IsContext()) {
7200 context = Handle<Context>::cast(receiver);
7201 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007202 } else if (receiver->IsJSContextExtensionObject()) {
7203 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007204 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007205 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007206 }
7207
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007208 // 'eval' is bound in the global context, but it may have been overwritten.
7209 // Compare it to the builtin 'GlobalEval' function to make sure.
7210 if (*callee != Top::global_context()->global_eval_fun() ||
7211 !args[1]->IsString()) {
7212 return MakePair(*callee, Top::context()->global()->global_receiver());
7213 }
7214
7215 // Deal with a normal eval call with a string argument. Compile it
7216 // and return the compiled function bound in the local context.
7217 Handle<String> source = args.at<String>(1);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007218 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007219 source,
7220 Handle<Context>(Top::context()),
7221 Top::context()->IsGlobalContext(),
7222 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007223 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7224 callee = Factory::NewFunctionFromSharedFunctionInfo(
7225 shared,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007226 Handle<Context>(Top::context()),
7227 NOT_TENURED);
7228 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007229}
7230
7231
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007232static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7233 // This utility adjusts the property attributes for newly created Function
7234 // object ("new Function(...)") by changing the map.
7235 // All it does is changing the prototype property to enumerable
7236 // as specified in ECMA262, 15.3.5.2.
7237 HandleScope scope;
7238 ASSERT(args.length() == 1);
7239 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7240 ASSERT(func->map()->instance_type() ==
7241 Top::function_instance_map()->instance_type());
7242 ASSERT(func->map()->instance_size() ==
7243 Top::function_instance_map()->instance_size());
7244 func->set_map(*Top::function_instance_map());
7245 return *func;
7246}
7247
7248
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007249// Push an array unto an array of arrays if it is not already in the
7250// array. Returns true if the element was pushed on the stack and
7251// false otherwise.
7252static Object* Runtime_PushIfAbsent(Arguments args) {
7253 ASSERT(args.length() == 2);
7254 CONVERT_CHECKED(JSArray, array, args[0]);
7255 CONVERT_CHECKED(JSArray, element, args[1]);
7256 RUNTIME_ASSERT(array->HasFastElements());
7257 int length = Smi::cast(array->length())->value();
7258 FixedArray* elements = FixedArray::cast(array->elements());
7259 for (int i = 0; i < length; i++) {
7260 if (elements->get(i) == element) return Heap::false_value();
7261 }
7262 Object* obj = array->SetFastElement(length, element);
7263 if (obj->IsFailure()) return obj;
7264 return Heap::true_value();
7265}
7266
7267
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007268/**
7269 * A simple visitor visits every element of Array's.
7270 * The backend storage can be a fixed array for fast elements case,
7271 * or a dictionary for sparse array. Since Dictionary is a subtype
7272 * of FixedArray, the class can be used by both fast and slow cases.
7273 * The second parameter of the constructor, fast_elements, specifies
7274 * whether the storage is a FixedArray or Dictionary.
7275 *
7276 * An index limit is used to deal with the situation that a result array
7277 * length overflows 32-bit non-negative integer.
7278 */
7279class ArrayConcatVisitor {
7280 public:
7281 ArrayConcatVisitor(Handle<FixedArray> storage,
7282 uint32_t index_limit,
7283 bool fast_elements) :
7284 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007285 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007286
7287 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007288 if (i >= index_limit_ - index_offset_) return;
7289 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007290
7291 if (fast_elements_) {
7292 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7293 storage_->set(index, *elm);
7294
7295 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007296 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7297 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007298 Factory::DictionaryAtNumberPut(dict, index, elm);
7299 if (!result.is_identical_to(dict))
7300 storage_ = result;
7301 }
7302 }
7303
7304 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007305 if (index_limit_ - index_offset_ < delta) {
7306 index_offset_ = index_limit_;
7307 } else {
7308 index_offset_ += delta;
7309 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007310 }
7311
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007312 Handle<FixedArray> storage() { return storage_; }
7313
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007314 private:
7315 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007316 // Limit on the accepted indices. Elements with indices larger than the
7317 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007318 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007319 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007320 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007321 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007322};
7323
7324
ager@chromium.org3811b432009-10-28 14:53:37 +00007325template<class ExternalArrayClass, class ElementType>
7326static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7327 bool elements_are_ints,
7328 bool elements_are_guaranteed_smis,
7329 uint32_t range,
7330 ArrayConcatVisitor* visitor) {
7331 Handle<ExternalArrayClass> array(
7332 ExternalArrayClass::cast(receiver->elements()));
7333 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7334
7335 if (visitor != NULL) {
7336 if (elements_are_ints) {
7337 if (elements_are_guaranteed_smis) {
7338 for (uint32_t j = 0; j < len; j++) {
7339 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7340 visitor->visit(j, e);
7341 }
7342 } else {
7343 for (uint32_t j = 0; j < len; j++) {
7344 int64_t val = static_cast<int64_t>(array->get(j));
7345 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7346 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7347 visitor->visit(j, e);
7348 } else {
7349 Handle<Object> e(
7350 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7351 visitor->visit(j, e);
7352 }
7353 }
7354 }
7355 } else {
7356 for (uint32_t j = 0; j < len; j++) {
7357 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7358 visitor->visit(j, e);
7359 }
7360 }
7361 }
7362
7363 return len;
7364}
7365
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007366/**
7367 * A helper function that visits elements of a JSObject. Only elements
7368 * whose index between 0 and range (exclusive) are visited.
7369 *
7370 * If the third parameter, visitor, is not NULL, the visitor is called
7371 * with parameters, 'visitor_index_offset + element index' and the element.
7372 *
7373 * It returns the number of visisted elements.
7374 */
7375static uint32_t IterateElements(Handle<JSObject> receiver,
7376 uint32_t range,
7377 ArrayConcatVisitor* visitor) {
7378 uint32_t num_of_elements = 0;
7379
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007380 switch (receiver->GetElementsKind()) {
7381 case JSObject::FAST_ELEMENTS: {
7382 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7383 uint32_t len = elements->length();
7384 if (range < len) {
7385 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007386 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007387
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007388 for (uint32_t j = 0; j < len; j++) {
7389 Handle<Object> e(elements->get(j));
7390 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007391 num_of_elements++;
7392 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007393 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007394 }
7395 }
7396 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007397 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007398 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007399 case JSObject::PIXEL_ELEMENTS: {
7400 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7401 uint32_t len = pixels->length();
7402 if (range < len) {
7403 len = range;
7404 }
7405
7406 for (uint32_t j = 0; j < len; j++) {
7407 num_of_elements++;
7408 if (visitor != NULL) {
7409 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7410 visitor->visit(j, e);
7411 }
7412 }
7413 break;
7414 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007415 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7416 num_of_elements =
7417 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7418 receiver, true, true, range, visitor);
7419 break;
7420 }
7421 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7422 num_of_elements =
7423 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7424 receiver, true, true, range, visitor);
7425 break;
7426 }
7427 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7428 num_of_elements =
7429 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7430 receiver, true, true, range, visitor);
7431 break;
7432 }
7433 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7434 num_of_elements =
7435 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7436 receiver, true, true, range, visitor);
7437 break;
7438 }
7439 case JSObject::EXTERNAL_INT_ELEMENTS: {
7440 num_of_elements =
7441 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7442 receiver, true, false, range, visitor);
7443 break;
7444 }
7445 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7446 num_of_elements =
7447 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7448 receiver, true, false, range, visitor);
7449 break;
7450 }
7451 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7452 num_of_elements =
7453 IterateExternalArrayElements<ExternalFloatArray, float>(
7454 receiver, false, false, range, visitor);
7455 break;
7456 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007457 case JSObject::DICTIONARY_ELEMENTS: {
7458 Handle<NumberDictionary> dict(receiver->element_dictionary());
7459 uint32_t capacity = dict->Capacity();
7460 for (uint32_t j = 0; j < capacity; j++) {
7461 Handle<Object> k(dict->KeyAt(j));
7462 if (dict->IsKey(*k)) {
7463 ASSERT(k->IsNumber());
7464 uint32_t index = static_cast<uint32_t>(k->Number());
7465 if (index < range) {
7466 num_of_elements++;
7467 if (visitor) {
7468 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7469 }
7470 }
7471 }
7472 }
7473 break;
7474 }
7475 default:
7476 UNREACHABLE();
7477 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007478 }
7479
7480 return num_of_elements;
7481}
7482
7483
7484/**
7485 * A helper function that visits elements of an Array object, and elements
7486 * on its prototypes.
7487 *
7488 * Elements on prototypes are visited first, and only elements whose indices
7489 * less than Array length are visited.
7490 *
7491 * If a ArrayConcatVisitor object is given, the visitor is called with
7492 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007493 *
7494 * The returned number of elements is an upper bound on the actual number
7495 * of elements added. If the same element occurs in more than one object
7496 * in the array's prototype chain, it will be counted more than once, but
7497 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007498 */
7499static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7500 ArrayConcatVisitor* visitor) {
7501 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7502 Handle<Object> obj = array;
7503
7504 static const int kEstimatedPrototypes = 3;
7505 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7506
7507 // Visit prototype first. If an element on the prototype is shadowed by
7508 // the inheritor using the same index, the ArrayConcatVisitor visits
7509 // the prototype element before the shadowing element.
7510 // The visitor can simply overwrite the old value by new value using
7511 // the same index. This follows Array::concat semantics.
7512 while (!obj->IsNull()) {
7513 objects.Add(Handle<JSObject>::cast(obj));
7514 obj = Handle<Object>(obj->GetPrototype());
7515 }
7516
7517 uint32_t nof_elements = 0;
7518 for (int i = objects.length() - 1; i >= 0; i--) {
7519 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007520 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007521 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007522
7523 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7524 nof_elements = JSObject::kMaxElementCount;
7525 } else {
7526 nof_elements += encountered_elements;
7527 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007528 }
7529
7530 return nof_elements;
7531}
7532
7533
7534/**
7535 * A helper function of Runtime_ArrayConcat.
7536 *
7537 * The first argument is an Array of arrays and objects. It is the
7538 * same as the arguments array of Array::concat JS function.
7539 *
7540 * If an argument is an Array object, the function visits array
7541 * elements. If an argument is not an Array object, the function
7542 * visits the object as if it is an one-element array.
7543 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007544 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007545 * non-negative number is used as new length. For example, if one
7546 * array length is 2^32 - 1, second array length is 1, the
7547 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007548 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7549 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007550 */
7551static uint32_t IterateArguments(Handle<JSArray> arguments,
7552 ArrayConcatVisitor* visitor) {
7553 uint32_t visited_elements = 0;
7554 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7555
7556 for (uint32_t i = 0; i < num_of_args; i++) {
7557 Handle<Object> obj(arguments->GetElement(i));
7558 if (obj->IsJSArray()) {
7559 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7560 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7561 uint32_t nof_elements =
7562 IterateArrayAndPrototypeElements(array, visitor);
7563 // Total elements of array and its prototype chain can be more than
7564 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007565 // the array length number of elements. We use the length as an estimate
7566 // for the actual number of elements added.
7567 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7568 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7569 visited_elements = JSArray::kMaxElementCount;
7570 } else {
7571 visited_elements += added_elements;
7572 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007573 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007574 } else {
7575 if (visitor) {
7576 visitor->visit(0, obj);
7577 visitor->increase_index_offset(1);
7578 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007579 if (visited_elements < JSArray::kMaxElementCount) {
7580 visited_elements++;
7581 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007582 }
7583 }
7584 return visited_elements;
7585}
7586
7587
7588/**
7589 * Array::concat implementation.
7590 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007591 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7592 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007593 */
7594static Object* Runtime_ArrayConcat(Arguments args) {
7595 ASSERT(args.length() == 1);
7596 HandleScope handle_scope;
7597
7598 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7599 Handle<JSArray> arguments(arg_arrays);
7600
7601 // Pass 1: estimate the number of elements of the result
7602 // (it could be more than real numbers if prototype has elements).
7603 uint32_t result_length = 0;
7604 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7605
7606 { AssertNoAllocation nogc;
7607 for (uint32_t i = 0; i < num_of_args; i++) {
7608 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007609 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007610 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007611 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007612 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7613 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007614 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007615 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007616 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7617 result_length = JSObject::kMaxElementCount;
7618 break;
7619 }
7620 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007621 }
7622 }
7623
7624 // Allocate an empty array, will set length and content later.
7625 Handle<JSArray> result = Factory::NewJSArray(0);
7626
7627 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7628 // If estimated number of elements is more than half of length, a
7629 // fixed array (fast case) is more time and space-efficient than a
7630 // dictionary.
7631 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7632
7633 Handle<FixedArray> storage;
7634 if (fast_case) {
7635 // The backing storage array must have non-existing elements to
7636 // preserve holes across concat operations.
7637 storage = Factory::NewFixedArrayWithHoles(result_length);
7638
7639 } else {
7640 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7641 uint32_t at_least_space_for = estimate_nof_elements +
7642 (estimate_nof_elements >> 2);
7643 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007644 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007645 }
7646
7647 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7648
7649 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7650
7651 IterateArguments(arguments, &visitor);
7652
7653 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007654 // Please note the storage might have changed in the visitor.
7655 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007656
7657 return *result;
7658}
7659
7660
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007661// This will not allocate (flatten the string), but it may run
7662// very slowly for very deeply nested ConsStrings. For debugging use only.
7663static Object* Runtime_GlobalPrint(Arguments args) {
7664 NoHandleAllocation ha;
7665 ASSERT(args.length() == 1);
7666
7667 CONVERT_CHECKED(String, string, args[0]);
7668 StringInputBuffer buffer(string);
7669 while (buffer.has_more()) {
7670 uint16_t character = buffer.GetNext();
7671 PrintF("%c", character);
7672 }
7673 return string;
7674}
7675
ager@chromium.org5ec48922009-05-05 07:25:34 +00007676// Moves all own elements of an object, that are below a limit, to positions
7677// starting at zero. All undefined values are placed after non-undefined values,
7678// and are followed by non-existing element. Does not change the length
7679// property.
7680// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007681static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007682 ASSERT(args.length() == 2);
7683 CONVERT_CHECKED(JSObject, object, args[0]);
7684 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7685 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007686}
7687
7688
7689// Move contents of argument 0 (an array) to argument 1 (an array)
7690static Object* Runtime_MoveArrayContents(Arguments args) {
7691 ASSERT(args.length() == 2);
7692 CONVERT_CHECKED(JSArray, from, args[0]);
7693 CONVERT_CHECKED(JSArray, to, args[1]);
7694 to->SetContent(FixedArray::cast(from->elements()));
7695 to->set_length(from->length());
7696 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007697 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007698 return to;
7699}
7700
7701
7702// How many elements does this array have?
7703static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7704 ASSERT(args.length() == 1);
7705 CONVERT_CHECKED(JSArray, array, args[0]);
7706 HeapObject* elements = array->elements();
7707 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007708 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007709 } else {
7710 return array->length();
7711 }
7712}
7713
7714
7715// Returns an array that tells you where in the [0, length) interval an array
7716// might have elements. Can either return keys or intervals. Keys can have
7717// gaps in (undefined). Intervals can also span over some undefined keys.
7718static Object* Runtime_GetArrayKeys(Arguments args) {
7719 ASSERT(args.length() == 2);
7720 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007721 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007722 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007723 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007724 // Create an array and get all the keys into it, then remove all the
7725 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007726 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007727 int keys_length = keys->length();
7728 for (int i = 0; i < keys_length; i++) {
7729 Object* key = keys->get(i);
7730 uint32_t index;
7731 if (!Array::IndexFromObject(key, &index) || index >= length) {
7732 // Zap invalid keys.
7733 keys->set_undefined(i);
7734 }
7735 }
7736 return *Factory::NewJSArrayWithElements(keys);
7737 } else {
7738 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7739 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007740 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00007741 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
7742 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007743 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007744 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007745 single_interval->set(1, *length_object);
7746 return *Factory::NewJSArrayWithElements(single_interval);
7747 }
7748}
7749
7750
7751// DefineAccessor takes an optional final argument which is the
7752// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7753// to the way accessors are implemented, it is set for both the getter
7754// and setter on the first call to DefineAccessor and ignored on
7755// subsequent calls.
7756static Object* Runtime_DefineAccessor(Arguments args) {
7757 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7758 // Compute attributes.
7759 PropertyAttributes attributes = NONE;
7760 if (args.length() == 5) {
7761 CONVERT_CHECKED(Smi, attrs, args[4]);
7762 int value = attrs->value();
7763 // Only attribute bits should be set.
7764 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7765 attributes = static_cast<PropertyAttributes>(value);
7766 }
7767
7768 CONVERT_CHECKED(JSObject, obj, args[0]);
7769 CONVERT_CHECKED(String, name, args[1]);
7770 CONVERT_CHECKED(Smi, flag, args[2]);
7771 CONVERT_CHECKED(JSFunction, fun, args[3]);
7772 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7773}
7774
7775
7776static Object* Runtime_LookupAccessor(Arguments args) {
7777 ASSERT(args.length() == 3);
7778 CONVERT_CHECKED(JSObject, obj, args[0]);
7779 CONVERT_CHECKED(String, name, args[1]);
7780 CONVERT_CHECKED(Smi, flag, args[2]);
7781 return obj->LookupAccessor(name, flag->value() == 0);
7782}
7783
7784
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007785#ifdef ENABLE_DEBUGGER_SUPPORT
7786static Object* Runtime_DebugBreak(Arguments args) {
7787 ASSERT(args.length() == 0);
7788 return Execution::DebugBreakHelper();
7789}
7790
7791
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007792// Helper functions for wrapping and unwrapping stack frame ids.
7793static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007794 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007795 return Smi::FromInt(id >> 2);
7796}
7797
7798
7799static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7800 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7801}
7802
7803
7804// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007805// args[0]: debug event listener function to set or null or undefined for
7806// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007807// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007808static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007809 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007810 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7811 args[0]->IsUndefined() ||
7812 args[0]->IsNull());
7813 Handle<Object> callback = args.at<Object>(0);
7814 Handle<Object> data = args.at<Object>(1);
7815 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007816
7817 return Heap::undefined_value();
7818}
7819
7820
7821static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007822 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007823 StackGuard::DebugBreak();
7824 return Heap::undefined_value();
7825}
7826
7827
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007828static Object* DebugLookupResultValue(Object* receiver, String* name,
7829 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007830 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007831 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007832 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007833 case NORMAL:
7834 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007835 if (value->IsTheHole()) {
7836 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007837 }
7838 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007839 case FIELD:
7840 value =
7841 JSObject::cast(
7842 result->holder())->FastPropertyAt(result->GetFieldIndex());
7843 if (value->IsTheHole()) {
7844 return Heap::undefined_value();
7845 }
7846 return value;
7847 case CONSTANT_FUNCTION:
7848 return result->GetConstantFunction();
7849 case CALLBACKS: {
7850 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007851 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007852 value = receiver->GetPropertyWithCallback(
7853 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007854 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007855 value = Top::pending_exception();
7856 Top::clear_pending_exception();
7857 if (caught_exception != NULL) {
7858 *caught_exception = true;
7859 }
7860 }
7861 return value;
7862 } else {
7863 return Heap::undefined_value();
7864 }
7865 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007866 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007867 case MAP_TRANSITION:
7868 case CONSTANT_TRANSITION:
7869 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007870 return Heap::undefined_value();
7871 default:
7872 UNREACHABLE();
7873 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007874 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007875 return Heap::undefined_value();
7876}
7877
7878
ager@chromium.org32912102009-01-16 10:38:43 +00007879// Get debugger related details for an object property.
7880// args[0]: object holding property
7881// args[1]: name of the property
7882//
7883// The array returned contains the following information:
7884// 0: Property value
7885// 1: Property details
7886// 2: Property value is exception
7887// 3: Getter function if defined
7888// 4: Setter function if defined
7889// Items 2-4 are only filled if the property has either a getter or a setter
7890// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007891static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007892 HandleScope scope;
7893
7894 ASSERT(args.length() == 2);
7895
7896 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7897 CONVERT_ARG_CHECKED(String, name, 1);
7898
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007899 // Make sure to set the current context to the context before the debugger was
7900 // entered (if the debugger is entered). The reason for switching context here
7901 // is that for some property lookups (accessors and interceptors) callbacks
7902 // into the embedding application can occour, and the embedding application
7903 // could have the assumption that its own global context is the current
7904 // context and not some internal debugger context.
7905 SaveContext save;
7906 if (Debug::InDebugger()) {
7907 Top::set_context(*Debug::debugger_entry()->GetContext());
7908 }
7909
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007910 // Skip the global proxy as it has no properties and always delegates to the
7911 // real global object.
7912 if (obj->IsJSGlobalProxy()) {
7913 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
7914 }
7915
7916
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007917 // Check if the name is trivially convertible to an index and get the element
7918 // if so.
7919 uint32_t index;
7920 if (name->AsArrayIndex(&index)) {
7921 Handle<FixedArray> details = Factory::NewFixedArray(2);
7922 details->set(0, Runtime::GetElementOrCharAt(obj, index));
7923 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
7924 return *Factory::NewJSArrayWithElements(details);
7925 }
7926
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007927 // Find the number of objects making up this.
7928 int length = LocalPrototypeChainLength(*obj);
7929
7930 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007931 Handle<JSObject> jsproto = obj;
7932 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007933 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007934 jsproto->LocalLookup(*name, &result);
7935 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007936 // LookupResult is not GC safe as it holds raw object pointers.
7937 // GC can happen later in this code so put the required fields into
7938 // local variables using handles when required for later use.
7939 PropertyType result_type = result.type();
7940 Handle<Object> result_callback_obj;
7941 if (result_type == CALLBACKS) {
7942 result_callback_obj = Handle<Object>(result.GetCallbackObject());
7943 }
7944 Smi* property_details = result.GetPropertyDetails().AsSmi();
7945 // DebugLookupResultValue can cause GC so details from LookupResult needs
7946 // to be copied to handles before this.
7947 bool caught_exception = false;
7948 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
7949 &caught_exception);
7950 if (raw_value->IsFailure()) return raw_value;
7951 Handle<Object> value(raw_value);
7952
7953 // If the callback object is a fixed array then it contains JavaScript
7954 // getter and/or setter.
7955 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
7956 result_callback_obj->IsFixedArray();
7957 Handle<FixedArray> details =
7958 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
7959 details->set(0, *value);
7960 details->set(1, property_details);
7961 if (hasJavaScriptAccessors) {
7962 details->set(2,
7963 caught_exception ? Heap::true_value()
7964 : Heap::false_value());
7965 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
7966 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
7967 }
7968
7969 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007970 }
7971 if (i < length - 1) {
7972 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
7973 }
7974 }
7975
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007976 return Heap::undefined_value();
7977}
7978
7979
7980static Object* Runtime_DebugGetProperty(Arguments args) {
7981 HandleScope scope;
7982
7983 ASSERT(args.length() == 2);
7984
7985 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7986 CONVERT_ARG_CHECKED(String, name, 1);
7987
7988 LookupResult result;
7989 obj->Lookup(*name, &result);
7990 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007991 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007992 }
7993 return Heap::undefined_value();
7994}
7995
7996
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007997// Return the property type calculated from the property details.
7998// args[0]: smi with property details.
7999static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8000 ASSERT(args.length() == 1);
8001 CONVERT_CHECKED(Smi, details, args[0]);
8002 PropertyType type = PropertyDetails(details).type();
8003 return Smi::FromInt(static_cast<int>(type));
8004}
8005
8006
8007// Return the property attribute calculated from the property details.
8008// args[0]: smi with property details.
8009static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8010 ASSERT(args.length() == 1);
8011 CONVERT_CHECKED(Smi, details, args[0]);
8012 PropertyAttributes attributes = PropertyDetails(details).attributes();
8013 return Smi::FromInt(static_cast<int>(attributes));
8014}
8015
8016
8017// Return the property insertion index calculated from the property details.
8018// args[0]: smi with property details.
8019static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8020 ASSERT(args.length() == 1);
8021 CONVERT_CHECKED(Smi, details, args[0]);
8022 int index = PropertyDetails(details).index();
8023 return Smi::FromInt(index);
8024}
8025
8026
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008027// Return property value from named interceptor.
8028// args[0]: object
8029// args[1]: property name
8030static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8031 HandleScope scope;
8032 ASSERT(args.length() == 2);
8033 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8034 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8035 CONVERT_ARG_CHECKED(String, name, 1);
8036
8037 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008038 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008039}
8040
8041
8042// Return element value from indexed interceptor.
8043// args[0]: object
8044// args[1]: index
8045static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8046 HandleScope scope;
8047 ASSERT(args.length() == 2);
8048 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8049 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8050 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8051
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008052 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008053}
8054
8055
8056static Object* Runtime_CheckExecutionState(Arguments args) {
8057 ASSERT(args.length() >= 1);
8058 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008059 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008060 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008061 return Top::Throw(Heap::illegal_execution_state_symbol());
8062 }
8063
8064 return Heap::true_value();
8065}
8066
8067
8068static Object* Runtime_GetFrameCount(Arguments args) {
8069 HandleScope scope;
8070 ASSERT(args.length() == 1);
8071
8072 // Check arguments.
8073 Object* result = Runtime_CheckExecutionState(args);
8074 if (result->IsFailure()) return result;
8075
8076 // Count all frames which are relevant to debugging stack trace.
8077 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008078 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008079 if (id == StackFrame::NO_ID) {
8080 // If there is no JavaScript stack frame count is 0.
8081 return Smi::FromInt(0);
8082 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008083 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8084 return Smi::FromInt(n);
8085}
8086
8087
8088static const int kFrameDetailsFrameIdIndex = 0;
8089static const int kFrameDetailsReceiverIndex = 1;
8090static const int kFrameDetailsFunctionIndex = 2;
8091static const int kFrameDetailsArgumentCountIndex = 3;
8092static const int kFrameDetailsLocalCountIndex = 4;
8093static const int kFrameDetailsSourcePositionIndex = 5;
8094static const int kFrameDetailsConstructCallIndex = 6;
8095static const int kFrameDetailsDebuggerFrameIndex = 7;
8096static const int kFrameDetailsFirstDynamicIndex = 8;
8097
8098// Return an array with frame details
8099// args[0]: number: break id
8100// args[1]: number: frame index
8101//
8102// The array returned contains the following information:
8103// 0: Frame id
8104// 1: Receiver
8105// 2: Function
8106// 3: Argument count
8107// 4: Local count
8108// 5: Source position
8109// 6: Constructor call
8110// 7: Debugger frame
8111// Arguments name, value
8112// Locals name, value
8113static Object* Runtime_GetFrameDetails(Arguments args) {
8114 HandleScope scope;
8115 ASSERT(args.length() == 2);
8116
8117 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008118 Object* check = Runtime_CheckExecutionState(args);
8119 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008120 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8121
8122 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008123 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008124 if (id == StackFrame::NO_ID) {
8125 // If there are no JavaScript stack frames return undefined.
8126 return Heap::undefined_value();
8127 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008128 int count = 0;
8129 JavaScriptFrameIterator it(id);
8130 for (; !it.done(); it.Advance()) {
8131 if (count == index) break;
8132 count++;
8133 }
8134 if (it.done()) return Heap::undefined_value();
8135
8136 // Traverse the saved contexts chain to find the active context for the
8137 // selected frame.
8138 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008139 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008140 save = save->prev();
8141 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008142 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008143
8144 // Get the frame id.
8145 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8146
8147 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008148 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008149
8150 // Check for constructor frame.
8151 bool constructor = it.frame()->IsConstructor();
8152
8153 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008154 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008155 ScopeInfo<> info(*code);
8156
8157 // Get the context.
8158 Handle<Context> context(Context::cast(it.frame()->context()));
8159
8160 // Get the locals names and values into a temporary array.
8161 //
8162 // TODO(1240907): Hide compiler-introduced stack variables
8163 // (e.g. .result)? For users of the debugger, they will probably be
8164 // confusing.
8165 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8166 for (int i = 0; i < info.NumberOfLocals(); i++) {
8167 // Name of the local.
8168 locals->set(i * 2, *info.LocalName(i));
8169
8170 // Fetch the value of the local - either from the stack or from a
8171 // heap-allocated context.
8172 if (i < info.number_of_stack_slots()) {
8173 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8174 } else {
8175 Handle<String> name = info.LocalName(i);
8176 // Traverse the context chain to the function context as all local
8177 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008178 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008179 context = Handle<Context>(context->previous());
8180 }
8181 ASSERT(context->is_function_context());
8182 locals->set(i * 2 + 1,
8183 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8184 NULL)));
8185 }
8186 }
8187
8188 // Now advance to the arguments adapter frame (if any). If contains all
8189 // the provided parameters and
8190
8191 // Now advance to the arguments adapter frame (if any). It contains all
8192 // the provided parameters whereas the function frame always have the number
8193 // of arguments matching the functions parameters. The rest of the
8194 // information (except for what is collected above) is the same.
8195 it.AdvanceToArgumentsFrame();
8196
8197 // Find the number of arguments to fill. At least fill the number of
8198 // parameters for the function and fill more if more parameters are provided.
8199 int argument_count = info.number_of_parameters();
8200 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8201 argument_count = it.frame()->GetProvidedParametersCount();
8202 }
8203
8204 // Calculate the size of the result.
8205 int details_size = kFrameDetailsFirstDynamicIndex +
8206 2 * (argument_count + info.NumberOfLocals());
8207 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8208
8209 // Add the frame id.
8210 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8211
8212 // Add the function (same as in function frame).
8213 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8214
8215 // Add the arguments count.
8216 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8217
8218 // Add the locals count
8219 details->set(kFrameDetailsLocalCountIndex,
8220 Smi::FromInt(info.NumberOfLocals()));
8221
8222 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008223 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008224 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8225 } else {
8226 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8227 }
8228
8229 // Add the constructor information.
8230 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8231
8232 // Add information on whether this frame is invoked in the debugger context.
8233 details->set(kFrameDetailsDebuggerFrameIndex,
8234 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8235
8236 // Fill the dynamic part.
8237 int details_index = kFrameDetailsFirstDynamicIndex;
8238
8239 // Add arguments name and value.
8240 for (int i = 0; i < argument_count; i++) {
8241 // Name of the argument.
8242 if (i < info.number_of_parameters()) {
8243 details->set(details_index++, *info.parameter_name(i));
8244 } else {
8245 details->set(details_index++, Heap::undefined_value());
8246 }
8247
8248 // Parameter value.
8249 if (i < it.frame()->GetProvidedParametersCount()) {
8250 details->set(details_index++, it.frame()->GetParameter(i));
8251 } else {
8252 details->set(details_index++, Heap::undefined_value());
8253 }
8254 }
8255
8256 // Add locals name and value from the temporary copy from the function frame.
8257 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8258 details->set(details_index++, locals->get(i));
8259 }
8260
8261 // Add the receiver (same as in function frame).
8262 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8263 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8264 Handle<Object> receiver(it.frame()->receiver());
8265 if (!receiver->IsJSObject()) {
8266 // If the receiver is NOT a JSObject we have hit an optimization
8267 // where a value object is not converted into a wrapped JS objects.
8268 // To hide this optimization from the debugger, we wrap the receiver
8269 // by creating correct wrapper object based on the calling frame's
8270 // global context.
8271 it.Advance();
8272 Handle<Context> calling_frames_global_context(
8273 Context::cast(Context::cast(it.frame()->context())->global_context()));
8274 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8275 }
8276 details->set(kFrameDetailsReceiverIndex, *receiver);
8277
8278 ASSERT_EQ(details_size, details_index);
8279 return *Factory::NewJSArrayWithElements(details);
8280}
8281
8282
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008283// Copy all the context locals into an object used to materialize a scope.
8284static void CopyContextLocalsToScopeObject(Handle<Code> code,
8285 ScopeInfo<>& scope_info,
8286 Handle<Context> context,
8287 Handle<JSObject> scope_object) {
8288 // Fill all context locals to the context extension.
8289 for (int i = Context::MIN_CONTEXT_SLOTS;
8290 i < scope_info.number_of_context_slots();
8291 i++) {
8292 int context_index =
8293 ScopeInfo<>::ContextSlotIndex(*code,
8294 *scope_info.context_slot_name(i),
8295 NULL);
8296
8297 // Don't include the arguments shadow (.arguments) context variable.
8298 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8299 SetProperty(scope_object,
8300 scope_info.context_slot_name(i),
8301 Handle<Object>(context->get(context_index)), NONE);
8302 }
8303 }
8304}
8305
8306
8307// Create a plain JSObject which materializes the local scope for the specified
8308// frame.
8309static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8310 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8311 Handle<Code> code(function->code());
8312 ScopeInfo<> scope_info(*code);
8313
8314 // Allocate and initialize a JSObject with all the arguments, stack locals
8315 // heap locals and extension properties of the debugged function.
8316 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8317
8318 // First fill all parameters.
8319 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8320 SetProperty(local_scope,
8321 scope_info.parameter_name(i),
8322 Handle<Object>(frame->GetParameter(i)), NONE);
8323 }
8324
8325 // Second fill all stack locals.
8326 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8327 SetProperty(local_scope,
8328 scope_info.stack_slot_name(i),
8329 Handle<Object>(frame->GetExpression(i)), NONE);
8330 }
8331
8332 // Third fill all context locals.
8333 Handle<Context> frame_context(Context::cast(frame->context()));
8334 Handle<Context> function_context(frame_context->fcontext());
8335 CopyContextLocalsToScopeObject(code, scope_info,
8336 function_context, local_scope);
8337
8338 // Finally copy any properties from the function context extension. This will
8339 // be variables introduced by eval.
8340 if (function_context->closure() == *function) {
8341 if (function_context->has_extension() &&
8342 !function_context->IsGlobalContext()) {
8343 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008344 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008345 for (int i = 0; i < keys->length(); i++) {
8346 // Names of variables introduced by eval are strings.
8347 ASSERT(keys->get(i)->IsString());
8348 Handle<String> key(String::cast(keys->get(i)));
8349 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8350 }
8351 }
8352 }
8353 return local_scope;
8354}
8355
8356
8357// Create a plain JSObject which materializes the closure content for the
8358// context.
8359static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8360 ASSERT(context->is_function_context());
8361
8362 Handle<Code> code(context->closure()->code());
8363 ScopeInfo<> scope_info(*code);
8364
8365 // Allocate and initialize a JSObject with all the content of theis function
8366 // closure.
8367 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8368
8369 // Check whether the arguments shadow object exists.
8370 int arguments_shadow_index =
8371 ScopeInfo<>::ContextSlotIndex(*code,
8372 Heap::arguments_shadow_symbol(),
8373 NULL);
8374 if (arguments_shadow_index >= 0) {
8375 // In this case all the arguments are available in the arguments shadow
8376 // object.
8377 Handle<JSObject> arguments_shadow(
8378 JSObject::cast(context->get(arguments_shadow_index)));
8379 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8380 SetProperty(closure_scope,
8381 scope_info.parameter_name(i),
8382 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8383 }
8384 }
8385
8386 // Fill all context locals to the context extension.
8387 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8388
8389 // Finally copy any properties from the function context extension. This will
8390 // be variables introduced by eval.
8391 if (context->has_extension()) {
8392 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008393 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008394 for (int i = 0; i < keys->length(); i++) {
8395 // Names of variables introduced by eval are strings.
8396 ASSERT(keys->get(i)->IsString());
8397 Handle<String> key(String::cast(keys->get(i)));
8398 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8399 }
8400 }
8401
8402 return closure_scope;
8403}
8404
8405
8406// Iterate over the actual scopes visible from a stack frame. All scopes are
8407// backed by an actual context except the local scope, which is inserted
8408// "artifically" in the context chain.
8409class ScopeIterator {
8410 public:
8411 enum ScopeType {
8412 ScopeTypeGlobal = 0,
8413 ScopeTypeLocal,
8414 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008415 ScopeTypeClosure,
8416 // Every catch block contains an implicit with block (its parameter is
8417 // a JSContextExtensionObject) that extends current scope with a variable
8418 // holding exception object. Such with blocks are treated as scopes of their
8419 // own type.
8420 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008421 };
8422
8423 explicit ScopeIterator(JavaScriptFrame* frame)
8424 : frame_(frame),
8425 function_(JSFunction::cast(frame->function())),
8426 context_(Context::cast(frame->context())),
8427 local_done_(false),
8428 at_local_(false) {
8429
8430 // Check whether the first scope is actually a local scope.
8431 if (context_->IsGlobalContext()) {
8432 // If there is a stack slot for .result then this local scope has been
8433 // created for evaluating top level code and it is not a real local scope.
8434 // Checking for the existence of .result seems fragile, but the scope info
8435 // saved with the code object does not otherwise have that information.
8436 Handle<Code> code(function_->code());
8437 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8438 at_local_ = index < 0;
8439 } else if (context_->is_function_context()) {
8440 at_local_ = true;
8441 }
8442 }
8443
8444 // More scopes?
8445 bool Done() { return context_.is_null(); }
8446
8447 // Move to the next scope.
8448 void Next() {
8449 // If at a local scope mark the local scope as passed.
8450 if (at_local_) {
8451 at_local_ = false;
8452 local_done_ = true;
8453
8454 // If the current context is not associated with the local scope the
8455 // current context is the next real scope, so don't move to the next
8456 // context in this case.
8457 if (context_->closure() != *function_) {
8458 return;
8459 }
8460 }
8461
8462 // The global scope is always the last in the chain.
8463 if (context_->IsGlobalContext()) {
8464 context_ = Handle<Context>();
8465 return;
8466 }
8467
8468 // Move to the next context.
8469 if (context_->is_function_context()) {
8470 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8471 } else {
8472 context_ = Handle<Context>(context_->previous());
8473 }
8474
8475 // If passing the local scope indicate that the current scope is now the
8476 // local scope.
8477 if (!local_done_ &&
8478 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8479 at_local_ = true;
8480 }
8481 }
8482
8483 // Return the type of the current scope.
8484 int Type() {
8485 if (at_local_) {
8486 return ScopeTypeLocal;
8487 }
8488 if (context_->IsGlobalContext()) {
8489 ASSERT(context_->global()->IsGlobalObject());
8490 return ScopeTypeGlobal;
8491 }
8492 if (context_->is_function_context()) {
8493 return ScopeTypeClosure;
8494 }
8495 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008496 // Current scope is either an explicit with statement or a with statement
8497 // implicitely generated for a catch block.
8498 // If the extension object here is a JSContextExtensionObject then
8499 // current with statement is one frome a catch block otherwise it's a
8500 // regular with statement.
8501 if (context_->extension()->IsJSContextExtensionObject()) {
8502 return ScopeTypeCatch;
8503 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008504 return ScopeTypeWith;
8505 }
8506
8507 // Return the JavaScript object with the content of the current scope.
8508 Handle<JSObject> ScopeObject() {
8509 switch (Type()) {
8510 case ScopeIterator::ScopeTypeGlobal:
8511 return Handle<JSObject>(CurrentContext()->global());
8512 break;
8513 case ScopeIterator::ScopeTypeLocal:
8514 // Materialize the content of the local scope into a JSObject.
8515 return MaterializeLocalScope(frame_);
8516 break;
8517 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008518 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008519 // Return the with object.
8520 return Handle<JSObject>(CurrentContext()->extension());
8521 break;
8522 case ScopeIterator::ScopeTypeClosure:
8523 // Materialize the content of the closure scope into a JSObject.
8524 return MaterializeClosure(CurrentContext());
8525 break;
8526 }
8527 UNREACHABLE();
8528 return Handle<JSObject>();
8529 }
8530
8531 // Return the context for this scope. For the local context there might not
8532 // be an actual context.
8533 Handle<Context> CurrentContext() {
8534 if (at_local_ && context_->closure() != *function_) {
8535 return Handle<Context>();
8536 }
8537 return context_;
8538 }
8539
8540#ifdef DEBUG
8541 // Debug print of the content of the current scope.
8542 void DebugPrint() {
8543 switch (Type()) {
8544 case ScopeIterator::ScopeTypeGlobal:
8545 PrintF("Global:\n");
8546 CurrentContext()->Print();
8547 break;
8548
8549 case ScopeIterator::ScopeTypeLocal: {
8550 PrintF("Local:\n");
8551 Handle<Code> code(function_->code());
8552 ScopeInfo<> scope_info(*code);
8553 scope_info.Print();
8554 if (!CurrentContext().is_null()) {
8555 CurrentContext()->Print();
8556 if (CurrentContext()->has_extension()) {
8557 Handle<JSObject> extension =
8558 Handle<JSObject>(CurrentContext()->extension());
8559 if (extension->IsJSContextExtensionObject()) {
8560 extension->Print();
8561 }
8562 }
8563 }
8564 break;
8565 }
8566
8567 case ScopeIterator::ScopeTypeWith: {
8568 PrintF("With:\n");
8569 Handle<JSObject> extension =
8570 Handle<JSObject>(CurrentContext()->extension());
8571 extension->Print();
8572 break;
8573 }
8574
ager@chromium.orga1645e22009-09-09 19:27:10 +00008575 case ScopeIterator::ScopeTypeCatch: {
8576 PrintF("Catch:\n");
8577 Handle<JSObject> extension =
8578 Handle<JSObject>(CurrentContext()->extension());
8579 extension->Print();
8580 break;
8581 }
8582
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008583 case ScopeIterator::ScopeTypeClosure: {
8584 PrintF("Closure:\n");
8585 CurrentContext()->Print();
8586 if (CurrentContext()->has_extension()) {
8587 Handle<JSObject> extension =
8588 Handle<JSObject>(CurrentContext()->extension());
8589 if (extension->IsJSContextExtensionObject()) {
8590 extension->Print();
8591 }
8592 }
8593 break;
8594 }
8595
8596 default:
8597 UNREACHABLE();
8598 }
8599 PrintF("\n");
8600 }
8601#endif
8602
8603 private:
8604 JavaScriptFrame* frame_;
8605 Handle<JSFunction> function_;
8606 Handle<Context> context_;
8607 bool local_done_;
8608 bool at_local_;
8609
8610 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8611};
8612
8613
8614static Object* Runtime_GetScopeCount(Arguments args) {
8615 HandleScope scope;
8616 ASSERT(args.length() == 2);
8617
8618 // Check arguments.
8619 Object* check = Runtime_CheckExecutionState(args);
8620 if (check->IsFailure()) return check;
8621 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8622
8623 // Get the frame where the debugging is performed.
8624 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8625 JavaScriptFrameIterator it(id);
8626 JavaScriptFrame* frame = it.frame();
8627
8628 // Count the visible scopes.
8629 int n = 0;
8630 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8631 n++;
8632 }
8633
8634 return Smi::FromInt(n);
8635}
8636
8637
8638static const int kScopeDetailsTypeIndex = 0;
8639static const int kScopeDetailsObjectIndex = 1;
8640static const int kScopeDetailsSize = 2;
8641
8642// Return an array with scope details
8643// args[0]: number: break id
8644// args[1]: number: frame index
8645// args[2]: number: scope index
8646//
8647// The array returned contains the following information:
8648// 0: Scope type
8649// 1: Scope object
8650static Object* Runtime_GetScopeDetails(Arguments args) {
8651 HandleScope scope;
8652 ASSERT(args.length() == 3);
8653
8654 // Check arguments.
8655 Object* check = Runtime_CheckExecutionState(args);
8656 if (check->IsFailure()) return check;
8657 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8658 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8659
8660 // Get the frame where the debugging is performed.
8661 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8662 JavaScriptFrameIterator frame_it(id);
8663 JavaScriptFrame* frame = frame_it.frame();
8664
8665 // Find the requested scope.
8666 int n = 0;
8667 ScopeIterator it(frame);
8668 for (; !it.Done() && n < index; it.Next()) {
8669 n++;
8670 }
8671 if (it.Done()) {
8672 return Heap::undefined_value();
8673 }
8674
8675 // Calculate the size of the result.
8676 int details_size = kScopeDetailsSize;
8677 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8678
8679 // Fill in scope details.
8680 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8681 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8682
8683 return *Factory::NewJSArrayWithElements(details);
8684}
8685
8686
8687static Object* Runtime_DebugPrintScopes(Arguments args) {
8688 HandleScope scope;
8689 ASSERT(args.length() == 0);
8690
8691#ifdef DEBUG
8692 // Print the scopes for the top frame.
8693 StackFrameLocator locator;
8694 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8695 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8696 it.DebugPrint();
8697 }
8698#endif
8699 return Heap::undefined_value();
8700}
8701
8702
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008703static Object* Runtime_GetCFrames(Arguments args) {
8704 HandleScope scope;
8705 ASSERT(args.length() == 1);
8706 Object* result = Runtime_CheckExecutionState(args);
8707 if (result->IsFailure()) return result;
8708
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008709#if V8_HOST_ARCH_64_BIT
8710 UNIMPLEMENTED();
8711 return Heap::undefined_value();
8712#else
8713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008714 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008715 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8716 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008717 if (frames_count == OS::kStackWalkError) {
8718 return Heap::undefined_value();
8719 }
8720
8721 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8722 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8723 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8724 for (int i = 0; i < frames_count; i++) {
8725 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8726 frame_value->SetProperty(
8727 *address_str,
8728 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8729 NONE);
8730
8731 // Get the stack walk text for this frame.
8732 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008733 int frame_text_length = StrLength(frames[i].text);
8734 if (frame_text_length > 0) {
8735 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008736 frame_text = Factory::NewStringFromAscii(str);
8737 }
8738
8739 if (!frame_text.is_null()) {
8740 frame_value->SetProperty(*text_str, *frame_text, NONE);
8741 }
8742
8743 frames_array->set(i, *frame_value);
8744 }
8745 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008746#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008747}
8748
8749
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008750static Object* Runtime_GetThreadCount(Arguments args) {
8751 HandleScope scope;
8752 ASSERT(args.length() == 1);
8753
8754 // Check arguments.
8755 Object* result = Runtime_CheckExecutionState(args);
8756 if (result->IsFailure()) return result;
8757
8758 // Count all archived V8 threads.
8759 int n = 0;
8760 for (ThreadState* thread = ThreadState::FirstInUse();
8761 thread != NULL;
8762 thread = thread->Next()) {
8763 n++;
8764 }
8765
8766 // Total number of threads is current thread and archived threads.
8767 return Smi::FromInt(n + 1);
8768}
8769
8770
8771static const int kThreadDetailsCurrentThreadIndex = 0;
8772static const int kThreadDetailsThreadIdIndex = 1;
8773static const int kThreadDetailsSize = 2;
8774
8775// Return an array with thread details
8776// args[0]: number: break id
8777// args[1]: number: thread index
8778//
8779// The array returned contains the following information:
8780// 0: Is current thread?
8781// 1: Thread id
8782static Object* Runtime_GetThreadDetails(Arguments args) {
8783 HandleScope scope;
8784 ASSERT(args.length() == 2);
8785
8786 // Check arguments.
8787 Object* check = Runtime_CheckExecutionState(args);
8788 if (check->IsFailure()) return check;
8789 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8790
8791 // Allocate array for result.
8792 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8793
8794 // Thread index 0 is current thread.
8795 if (index == 0) {
8796 // Fill the details.
8797 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8798 details->set(kThreadDetailsThreadIdIndex,
8799 Smi::FromInt(ThreadManager::CurrentId()));
8800 } else {
8801 // Find the thread with the requested index.
8802 int n = 1;
8803 ThreadState* thread = ThreadState::FirstInUse();
8804 while (index != n && thread != NULL) {
8805 thread = thread->Next();
8806 n++;
8807 }
8808 if (thread == NULL) {
8809 return Heap::undefined_value();
8810 }
8811
8812 // Fill the details.
8813 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8814 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8815 }
8816
8817 // Convert to JS array and return.
8818 return *Factory::NewJSArrayWithElements(details);
8819}
8820
8821
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008822static Object* Runtime_GetBreakLocations(Arguments args) {
8823 HandleScope scope;
8824 ASSERT(args.length() == 1);
8825
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008826 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8827 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008828 // Find the number of break points
8829 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8830 if (break_locations->IsUndefined()) return Heap::undefined_value();
8831 // Return array as JS array
8832 return *Factory::NewJSArrayWithElements(
8833 Handle<FixedArray>::cast(break_locations));
8834}
8835
8836
8837// Set a break point in a function
8838// args[0]: function
8839// args[1]: number: break source position (within the function source)
8840// args[2]: number: break point object
8841static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8842 HandleScope scope;
8843 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008844 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8845 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008846 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8847 RUNTIME_ASSERT(source_position >= 0);
8848 Handle<Object> break_point_object_arg = args.at<Object>(2);
8849
8850 // Set break point.
8851 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8852
8853 return Heap::undefined_value();
8854}
8855
8856
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008857Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8858 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008859 // Iterate the heap looking for SharedFunctionInfo generated from the
8860 // script. The inner most SharedFunctionInfo containing the source position
8861 // for the requested break point is found.
8862 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8863 // which is found is not compiled it is compiled and the heap is iterated
8864 // again as the compilation might create inner functions from the newly
8865 // compiled function and the actual requested break point might be in one of
8866 // these functions.
8867 bool done = false;
8868 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008869 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008870 Handle<SharedFunctionInfo> target;
8871 // The current candidate for the last function in script:
8872 Handle<SharedFunctionInfo> last;
8873 while (!done) {
8874 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008875 for (HeapObject* obj = iterator.next();
8876 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008877 if (obj->IsSharedFunctionInfo()) {
8878 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
8879 if (shared->script() == *script) {
8880 // If the SharedFunctionInfo found has the requested script data and
8881 // contains the source position it is a candidate.
8882 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00008883 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008884 start_position = shared->start_position();
8885 }
8886 if (start_position <= position &&
8887 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00008888 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008889 // candidate this is the new candidate.
8890 if (target.is_null()) {
8891 target_start_position = start_position;
8892 target = shared;
8893 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00008894 if (target_start_position == start_position &&
8895 shared->end_position() == target->end_position()) {
8896 // If a top-level function contain only one function
8897 // declartion the source for the top-level and the function is
8898 // the same. In that case prefer the non top-level function.
8899 if (!shared->is_toplevel()) {
8900 target_start_position = start_position;
8901 target = shared;
8902 }
8903 } else if (target_start_position <= start_position &&
8904 shared->end_position() <= target->end_position()) {
8905 // This containment check includes equality as a function inside
8906 // a top-level function can share either start or end position
8907 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008908 target_start_position = start_position;
8909 target = shared;
8910 }
8911 }
8912 }
8913
8914 // Keep track of the last function in the script.
8915 if (last.is_null() ||
8916 shared->end_position() > last->start_position()) {
8917 last = shared;
8918 }
8919 }
8920 }
8921 }
8922
8923 // Make sure some candidate is selected.
8924 if (target.is_null()) {
8925 if (!last.is_null()) {
8926 // Position after the last function - use last.
8927 target = last;
8928 } else {
8929 // Unable to find function - possibly script without any function.
8930 return Heap::undefined_value();
8931 }
8932 }
8933
8934 // If the candidate found is compiled we are done. NOTE: when lazy
8935 // compilation of inner functions is introduced some additional checking
8936 // needs to be done here to compile inner functions.
8937 done = target->is_compiled();
8938 if (!done) {
8939 // If the candidate is not compiled compile it to reveal any inner
8940 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008941 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008942 }
8943 }
8944
8945 return *target;
8946}
8947
8948
8949// Change the state of a break point in a script. NOTE: Regarding performance
8950// see the NOTE for GetScriptFromScriptData.
8951// args[0]: script to set break point in
8952// args[1]: number: break source position (within the script source)
8953// args[2]: number: break point object
8954static Object* Runtime_SetScriptBreakPoint(Arguments args) {
8955 HandleScope scope;
8956 ASSERT(args.length() == 3);
8957 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
8958 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8959 RUNTIME_ASSERT(source_position >= 0);
8960 Handle<Object> break_point_object_arg = args.at<Object>(2);
8961
8962 // Get the script from the script wrapper.
8963 RUNTIME_ASSERT(wrapper->value()->IsScript());
8964 Handle<Script> script(Script::cast(wrapper->value()));
8965
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008966 Object* result = Runtime::FindSharedFunctionInfoInScript(
8967 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008968 if (!result->IsUndefined()) {
8969 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
8970 // Find position within function. The script position might be before the
8971 // source position of the first function.
8972 int position;
8973 if (shared->start_position() > source_position) {
8974 position = 0;
8975 } else {
8976 position = source_position - shared->start_position();
8977 }
8978 Debug::SetBreakPoint(shared, position, break_point_object_arg);
8979 }
8980 return Heap::undefined_value();
8981}
8982
8983
8984// Clear a break point
8985// args[0]: number: break point object
8986static Object* Runtime_ClearBreakPoint(Arguments args) {
8987 HandleScope scope;
8988 ASSERT(args.length() == 1);
8989 Handle<Object> break_point_object_arg = args.at<Object>(0);
8990
8991 // Clear break point.
8992 Debug::ClearBreakPoint(break_point_object_arg);
8993
8994 return Heap::undefined_value();
8995}
8996
8997
8998// Change the state of break on exceptions
8999// args[0]: boolean indicating uncaught exceptions
9000// args[1]: boolean indicating on/off
9001static Object* Runtime_ChangeBreakOnException(Arguments args) {
9002 HandleScope scope;
9003 ASSERT(args.length() == 2);
9004 ASSERT(args[0]->IsNumber());
9005 ASSERT(args[1]->IsBoolean());
9006
9007 // Update break point state
9008 ExceptionBreakType type =
9009 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9010 bool enable = args[1]->ToBoolean()->IsTrue();
9011 Debug::ChangeBreakOnException(type, enable);
9012 return Heap::undefined_value();
9013}
9014
9015
9016// Prepare for stepping
9017// args[0]: break id for checking execution state
9018// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009019// args[2]: number of times to perform the step, for step out it is the number
9020// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009021static Object* Runtime_PrepareStep(Arguments args) {
9022 HandleScope scope;
9023 ASSERT(args.length() == 3);
9024 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009025 Object* check = Runtime_CheckExecutionState(args);
9026 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009027 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9028 return Top::Throw(Heap::illegal_argument_symbol());
9029 }
9030
9031 // Get the step action and check validity.
9032 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9033 if (step_action != StepIn &&
9034 step_action != StepNext &&
9035 step_action != StepOut &&
9036 step_action != StepInMin &&
9037 step_action != StepMin) {
9038 return Top::Throw(Heap::illegal_argument_symbol());
9039 }
9040
9041 // Get the number of steps.
9042 int step_count = NumberToInt32(args[2]);
9043 if (step_count < 1) {
9044 return Top::Throw(Heap::illegal_argument_symbol());
9045 }
9046
ager@chromium.orga1645e22009-09-09 19:27:10 +00009047 // Clear all current stepping setup.
9048 Debug::ClearStepping();
9049
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009050 // Prepare step.
9051 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9052 return Heap::undefined_value();
9053}
9054
9055
9056// Clear all stepping set by PrepareStep.
9057static Object* Runtime_ClearStepping(Arguments args) {
9058 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009059 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009060 Debug::ClearStepping();
9061 return Heap::undefined_value();
9062}
9063
9064
9065// Creates a copy of the with context chain. The copy of the context chain is
9066// is linked to the function context supplied.
9067static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9068 Handle<Context> function_context) {
9069 // At the bottom of the chain. Return the function context to link to.
9070 if (context_chain->is_function_context()) {
9071 return function_context;
9072 }
9073
9074 // Recursively copy the with contexts.
9075 Handle<Context> previous(context_chain->previous());
9076 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9077 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009078 CopyWithContextChain(function_context, previous),
9079 extension,
9080 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009081}
9082
9083
9084// Helper function to find or create the arguments object for
9085// Runtime_DebugEvaluate.
9086static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9087 Handle<JSFunction> function,
9088 Handle<Code> code,
9089 const ScopeInfo<>* sinfo,
9090 Handle<Context> function_context) {
9091 // Try to find the value of 'arguments' to pass as parameter. If it is not
9092 // found (that is the debugged function does not reference 'arguments' and
9093 // does not support eval) then create an 'arguments' object.
9094 int index;
9095 if (sinfo->number_of_stack_slots() > 0) {
9096 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9097 if (index != -1) {
9098 return Handle<Object>(frame->GetExpression(index));
9099 }
9100 }
9101
9102 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9103 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9104 NULL);
9105 if (index != -1) {
9106 return Handle<Object>(function_context->get(index));
9107 }
9108 }
9109
9110 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009111 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9112 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009113
9114 AssertNoAllocation no_gc;
9115 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009116 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009117 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009118 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009119 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009120 return arguments;
9121}
9122
9123
9124// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009125// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009126// extension part has all the parameters and locals of the function on the
9127// stack frame. A function which calls eval with the code to evaluate is then
9128// compiled in this context and called in this context. As this context
9129// replaces the context of the function on the stack frame a new (empty)
9130// function is created as well to be used as the closure for the context.
9131// This function and the context acts as replacements for the function on the
9132// stack frame presenting the same view of the values of parameters and
9133// local variables as if the piece of JavaScript was evaluated at the point
9134// where the function on the stack frame is currently stopped.
9135static Object* Runtime_DebugEvaluate(Arguments args) {
9136 HandleScope scope;
9137
9138 // Check the execution state and decode arguments frame and source to be
9139 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009140 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009141 Object* check_result = Runtime_CheckExecutionState(args);
9142 if (check_result->IsFailure()) return check_result;
9143 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9144 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009145 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9146
9147 // Handle the processing of break.
9148 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009149
9150 // Get the frame where the debugging is performed.
9151 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9152 JavaScriptFrameIterator it(id);
9153 JavaScriptFrame* frame = it.frame();
9154 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9155 Handle<Code> code(function->code());
9156 ScopeInfo<> sinfo(*code);
9157
9158 // Traverse the saved contexts chain to find the active context for the
9159 // selected frame.
9160 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009161 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009162 save = save->prev();
9163 }
9164 ASSERT(save != NULL);
9165 SaveContext savex;
9166 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009167
9168 // Create the (empty) function replacing the function on the stack frame for
9169 // the purpose of evaluating in the context created below. It is important
9170 // that this function does not describe any parameters and local variables
9171 // in the context. If it does then this will cause problems with the lookup
9172 // in Context::Lookup, where context slots for parameters and local variables
9173 // are looked at before the extension object.
9174 Handle<JSFunction> go_between =
9175 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9176 go_between->set_context(function->context());
9177#ifdef DEBUG
9178 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9179 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9180 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9181#endif
9182
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009183 // Materialize the content of the local scope into a JSObject.
9184 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009185
9186 // Allocate a new context for the debug evaluation and set the extension
9187 // object build.
9188 Handle<Context> context =
9189 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009190 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009191 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009192 Handle<Context> frame_context(Context::cast(frame->context()));
9193 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009194 context = CopyWithContextChain(frame_context, context);
9195
9196 // Wrap the evaluation statement in a new function compiled in the newly
9197 // created context. The function has one parameter which has to be called
9198 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009199 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009200 // function(arguments,__source__) {return eval(__source__);}
9201 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009202 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009203 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009204 Handle<String> function_source =
9205 Factory::NewStringFromAscii(Vector<const char>(source_str,
9206 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009207 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009208 Compiler::CompileEval(function_source,
9209 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009210 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009211 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009212 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009213 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009214 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009215
9216 // Invoke the result of the compilation to get the evaluation function.
9217 bool has_pending_exception;
9218 Handle<Object> receiver(frame->receiver());
9219 Handle<Object> evaluation_function =
9220 Execution::Call(compiled_function, receiver, 0, NULL,
9221 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009222 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009223
9224 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9225 function_context);
9226
9227 // Invoke the evaluation function and return the result.
9228 const int argc = 2;
9229 Object** argv[argc] = { arguments.location(),
9230 Handle<Object>::cast(source).location() };
9231 Handle<Object> result =
9232 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9233 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009234 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009235
9236 // Skip the global proxy as it has no properties and always delegates to the
9237 // real global object.
9238 if (result->IsJSGlobalProxy()) {
9239 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9240 }
9241
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009242 return *result;
9243}
9244
9245
9246static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9247 HandleScope scope;
9248
9249 // Check the execution state and decode arguments frame and source to be
9250 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009251 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009252 Object* check_result = Runtime_CheckExecutionState(args);
9253 if (check_result->IsFailure()) return check_result;
9254 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009255 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9256
9257 // Handle the processing of break.
9258 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009259
9260 // Enter the top context from before the debugger was invoked.
9261 SaveContext save;
9262 SaveContext* top = &save;
9263 while (top != NULL && *top->context() == *Debug::debug_context()) {
9264 top = top->prev();
9265 }
9266 if (top != NULL) {
9267 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009268 }
9269
9270 // Get the global context now set to the top context from before the
9271 // debugger was invoked.
9272 Handle<Context> context = Top::global_context();
9273
9274 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009275 Handle<SharedFunctionInfo> shared =
9276 Compiler::CompileEval(source,
9277 context,
9278 true,
9279 Compiler::DONT_VALIDATE_JSON);
9280 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009281 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009282 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9283 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009284
9285 // Invoke the result of the compilation to get the evaluation function.
9286 bool has_pending_exception;
9287 Handle<Object> receiver = Top::global();
9288 Handle<Object> result =
9289 Execution::Call(compiled_function, receiver, 0, NULL,
9290 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009291 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009292 return *result;
9293}
9294
9295
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009296static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9297 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009298 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009299
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009300 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009301 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009302
9303 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009304 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009305 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9306 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9307 // because using
9308 // instances->set(i, *GetScriptWrapper(script))
9309 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9310 // already have deferenced the instances handle.
9311 Handle<JSValue> wrapper = GetScriptWrapper(script);
9312 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009313 }
9314
9315 // Return result as a JS array.
9316 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9317 Handle<JSArray>::cast(result)->SetContent(*instances);
9318 return *result;
9319}
9320
9321
9322// Helper function used by Runtime_DebugReferencedBy below.
9323static int DebugReferencedBy(JSObject* target,
9324 Object* instance_filter, int max_references,
9325 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009326 JSFunction* arguments_function) {
9327 NoHandleAllocation ha;
9328 AssertNoAllocation no_alloc;
9329
9330 // Iterate the heap.
9331 int count = 0;
9332 JSObject* last = NULL;
9333 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009334 HeapObject* heap_obj = NULL;
9335 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009336 (max_references == 0 || count < max_references)) {
9337 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009338 if (heap_obj->IsJSObject()) {
9339 // Skip context extension objects and argument arrays as these are
9340 // checked in the context of functions using them.
9341 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009342 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009343 obj->map()->constructor() == arguments_function) {
9344 continue;
9345 }
9346
9347 // Check if the JS object has a reference to the object looked for.
9348 if (obj->ReferencesObject(target)) {
9349 // Check instance filter if supplied. This is normally used to avoid
9350 // references from mirror objects (see Runtime_IsInPrototypeChain).
9351 if (!instance_filter->IsUndefined()) {
9352 Object* V = obj;
9353 while (true) {
9354 Object* prototype = V->GetPrototype();
9355 if (prototype->IsNull()) {
9356 break;
9357 }
9358 if (instance_filter == prototype) {
9359 obj = NULL; // Don't add this object.
9360 break;
9361 }
9362 V = prototype;
9363 }
9364 }
9365
9366 if (obj != NULL) {
9367 // Valid reference found add to instance array if supplied an update
9368 // count.
9369 if (instances != NULL && count < instances_size) {
9370 instances->set(count, obj);
9371 }
9372 last = obj;
9373 count++;
9374 }
9375 }
9376 }
9377 }
9378
9379 // Check for circular reference only. This can happen when the object is only
9380 // referenced from mirrors and has a circular reference in which case the
9381 // object is not really alive and would have been garbage collected if not
9382 // referenced from the mirror.
9383 if (count == 1 && last == target) {
9384 count = 0;
9385 }
9386
9387 // Return the number of referencing objects found.
9388 return count;
9389}
9390
9391
9392// Scan the heap for objects with direct references to an object
9393// args[0]: the object to find references to
9394// args[1]: constructor function for instances to exclude (Mirror)
9395// args[2]: the the maximum number of objects to return
9396static Object* Runtime_DebugReferencedBy(Arguments args) {
9397 ASSERT(args.length() == 3);
9398
9399 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009400 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009401
9402 // Check parameters.
9403 CONVERT_CHECKED(JSObject, target, args[0]);
9404 Object* instance_filter = args[1];
9405 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9406 instance_filter->IsJSObject());
9407 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9408 RUNTIME_ASSERT(max_references >= 0);
9409
9410 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009411 JSObject* arguments_boilerplate =
9412 Top::context()->global_context()->arguments_boilerplate();
9413 JSFunction* arguments_function =
9414 JSFunction::cast(arguments_boilerplate->map()->constructor());
9415
9416 // Get the number of referencing objects.
9417 int count;
9418 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009419 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009420
9421 // Allocate an array to hold the result.
9422 Object* object = Heap::AllocateFixedArray(count);
9423 if (object->IsFailure()) return object;
9424 FixedArray* instances = FixedArray::cast(object);
9425
9426 // Fill the referencing objects.
9427 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009428 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009429
9430 // Return result as JS array.
9431 Object* result =
9432 Heap::AllocateJSObject(
9433 Top::context()->global_context()->array_function());
9434 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9435 return result;
9436}
9437
9438
9439// Helper function used by Runtime_DebugConstructedBy below.
9440static int DebugConstructedBy(JSFunction* constructor, int max_references,
9441 FixedArray* instances, int instances_size) {
9442 AssertNoAllocation no_alloc;
9443
9444 // Iterate the heap.
9445 int count = 0;
9446 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009447 HeapObject* heap_obj = NULL;
9448 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009449 (max_references == 0 || count < max_references)) {
9450 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009451 if (heap_obj->IsJSObject()) {
9452 JSObject* obj = JSObject::cast(heap_obj);
9453 if (obj->map()->constructor() == constructor) {
9454 // Valid reference found add to instance array if supplied an update
9455 // count.
9456 if (instances != NULL && count < instances_size) {
9457 instances->set(count, obj);
9458 }
9459 count++;
9460 }
9461 }
9462 }
9463
9464 // Return the number of referencing objects found.
9465 return count;
9466}
9467
9468
9469// Scan the heap for objects constructed by a specific function.
9470// args[0]: the constructor to find instances of
9471// args[1]: the the maximum number of objects to return
9472static Object* Runtime_DebugConstructedBy(Arguments args) {
9473 ASSERT(args.length() == 2);
9474
9475 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009476 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009477
9478 // Check parameters.
9479 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9480 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9481 RUNTIME_ASSERT(max_references >= 0);
9482
9483 // Get the number of referencing objects.
9484 int count;
9485 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9486
9487 // Allocate an array to hold the result.
9488 Object* object = Heap::AllocateFixedArray(count);
9489 if (object->IsFailure()) return object;
9490 FixedArray* instances = FixedArray::cast(object);
9491
9492 // Fill the referencing objects.
9493 count = DebugConstructedBy(constructor, max_references, instances, count);
9494
9495 // Return result as JS array.
9496 Object* result =
9497 Heap::AllocateJSObject(
9498 Top::context()->global_context()->array_function());
9499 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9500 return result;
9501}
9502
9503
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009504// Find the effective prototype object as returned by __proto__.
9505// args[0]: the object to find the prototype for.
9506static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009507 ASSERT(args.length() == 1);
9508
9509 CONVERT_CHECKED(JSObject, obj, args[0]);
9510
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009511 // Use the __proto__ accessor.
9512 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009513}
9514
9515
9516static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009517 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009518 CPU::DebugBreak();
9519 return Heap::undefined_value();
9520}
9521
9522
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009523static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009524#ifdef DEBUG
9525 HandleScope scope;
9526 ASSERT(args.length() == 1);
9527 // Get the function and make sure it is compiled.
9528 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009529 Handle<SharedFunctionInfo> shared(func->shared());
9530 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009531 return Failure::Exception();
9532 }
9533 func->code()->PrintLn();
9534#endif // DEBUG
9535 return Heap::undefined_value();
9536}
ager@chromium.org9085a012009-05-11 19:22:57 +00009537
9538
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009539static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9540#ifdef DEBUG
9541 HandleScope scope;
9542 ASSERT(args.length() == 1);
9543 // Get the function and make sure it is compiled.
9544 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009545 Handle<SharedFunctionInfo> shared(func->shared());
9546 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009547 return Failure::Exception();
9548 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009549 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009550#endif // DEBUG
9551 return Heap::undefined_value();
9552}
9553
9554
ager@chromium.org9085a012009-05-11 19:22:57 +00009555static Object* Runtime_FunctionGetInferredName(Arguments args) {
9556 NoHandleAllocation ha;
9557 ASSERT(args.length() == 1);
9558
9559 CONVERT_CHECKED(JSFunction, f, args[0]);
9560 return f->shared()->inferred_name();
9561}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009562
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009563
9564static int FindSharedFunctionInfosForScript(Script* script,
9565 FixedArray* buffer) {
9566 AssertNoAllocation no_allocations;
9567
9568 int counter = 0;
9569 int buffer_size = buffer->length();
9570 HeapIterator iterator;
9571 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9572 ASSERT(obj != NULL);
9573 if (!obj->IsSharedFunctionInfo()) {
9574 continue;
9575 }
9576 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9577 if (shared->script() != script) {
9578 continue;
9579 }
9580 if (counter < buffer_size) {
9581 buffer->set(counter, shared);
9582 }
9583 counter++;
9584 }
9585 return counter;
9586}
9587
9588// For a script finds all SharedFunctionInfo's in the heap that points
9589// to this script. Returns JSArray of SharedFunctionInfo wrapped
9590// in OpaqueReferences.
9591static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9592 Arguments args) {
9593 ASSERT(args.length() == 1);
9594 HandleScope scope;
9595 CONVERT_CHECKED(JSValue, script_value, args[0]);
9596
9597 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9598
9599 const int kBufferSize = 32;
9600
9601 Handle<FixedArray> array;
9602 array = Factory::NewFixedArray(kBufferSize);
9603 int number = FindSharedFunctionInfosForScript(*script, *array);
9604 if (number > kBufferSize) {
9605 array = Factory::NewFixedArray(number);
9606 FindSharedFunctionInfosForScript(*script, *array);
9607 }
9608
9609 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9610 result->set_length(Smi::FromInt(number));
9611
9612 LiveEdit::WrapSharedFunctionInfos(result);
9613
9614 return *result;
9615}
9616
9617// For a script calculates compilation information about all its functions.
9618// The script source is explicitly specified by the second argument.
9619// The source of the actual script is not used, however it is important that
9620// all generated code keeps references to this particular instance of script.
9621// Returns a JSArray of compilation infos. The array is ordered so that
9622// each function with all its descendant is always stored in a continues range
9623// with the function itself going first. The root function is a script function.
9624static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9625 ASSERT(args.length() == 2);
9626 HandleScope scope;
9627 CONVERT_CHECKED(JSValue, script, args[0]);
9628 CONVERT_ARG_CHECKED(String, source, 1);
9629 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9630
9631 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9632
9633 if (Top::has_pending_exception()) {
9634 return Failure::Exception();
9635 }
9636
9637 return result;
9638}
9639
9640// Changes the source of the script to a new_source and creates a new
9641// script representing the old version of the script source.
9642static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9643 ASSERT(args.length() == 3);
9644 HandleScope scope;
9645 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9646 CONVERT_ARG_CHECKED(String, new_source, 1);
9647 CONVERT_ARG_CHECKED(String, old_script_name, 2);
9648 Handle<Script> original_script =
9649 Handle<Script>(Script::cast(original_script_value->value()));
9650
9651 Handle<String> original_source(String::cast(original_script->source()));
9652
9653 original_script->set_source(*new_source);
9654 Handle<Script> old_script = Factory::NewScript(original_source);
9655 old_script->set_name(*old_script_name);
9656 old_script->set_line_offset(original_script->line_offset());
9657 old_script->set_column_offset(original_script->column_offset());
9658 old_script->set_data(original_script->data());
9659 old_script->set_type(original_script->type());
9660 old_script->set_context_data(original_script->context_data());
9661 old_script->set_compilation_type(original_script->compilation_type());
9662 old_script->set_eval_from_shared(original_script->eval_from_shared());
9663 old_script->set_eval_from_instructions_offset(
9664 original_script->eval_from_instructions_offset());
9665
ager@chromium.org357bf652010-04-12 11:30:10 +00009666 // Drop line ends so that they will be recalculated.
9667 original_script->set_line_ends(Heap::undefined_value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009668
9669 Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
9670
9671 return *(GetScriptWrapper(old_script));
9672}
9673
9674// Replaces code of SharedFunctionInfo with a new one.
9675static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9676 ASSERT(args.length() == 2);
9677 HandleScope scope;
9678 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9679 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9680
9681 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
9682
9683 return Heap::undefined_value();
9684}
9685
9686// Connects SharedFunctionInfo to another script.
9687static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
9688 ASSERT(args.length() == 2);
9689 HandleScope scope;
9690 CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
9691 CONVERT_ARG_CHECKED(JSValue, script_value, 1);
9692 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9693
9694 LiveEdit::RelinkFunctionToScript(shared_info_array, script);
9695
9696 return Heap::undefined_value();
9697}
9698
9699// Updates positions of a shared function info (first parameter) according
9700// to script source change. Text change is described in second parameter as
9701// array of groups of 3 numbers:
9702// (change_begin, change_end, change_end_new_position).
9703// Each group describes a change in text; groups are sorted by change_begin.
ager@chromium.org357bf652010-04-12 11:30:10 +00009704// Returns an array of pairs (new source position, breakpoint_object/array)
9705// so that JS side could update positions in breakpoint objects.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009706static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9707 ASSERT(args.length() == 2);
9708 HandleScope scope;
9709 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9710 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9711
ager@chromium.org357bf652010-04-12 11:30:10 +00009712 Handle<Object> result =
9713 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009714
ager@chromium.org357bf652010-04-12 11:30:10 +00009715 return *result;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009716}
9717
9718
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009719// For array of SharedFunctionInfo's (each wrapped in JSValue)
9720// checks that none of them have activations on stacks (of any thread).
9721// Returns array of the same length with corresponding results of
9722// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009723static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9724 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009725 HandleScope scope;
9726 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009727 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009728
9729
ager@chromium.org357bf652010-04-12 11:30:10 +00009730 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009731}
9732
9733
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009734// A testing entry. Returns statement position which is the closest to
9735// source_position.
9736static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9737 ASSERT(args.length() == 2);
9738 HandleScope scope;
9739 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9740 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9741
9742 Handle<Code> code(function->code());
9743
9744 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9745 int closest_pc = 0;
9746 int distance = kMaxInt;
9747 while (!it.done()) {
9748 int statement_position = static_cast<int>(it.rinfo()->data());
9749 // Check if this break point is closer that what was previously found.
9750 if (source_position <= statement_position &&
9751 statement_position - source_position < distance) {
9752 closest_pc = it.rinfo()->pc() - code->instruction_start();
9753 distance = statement_position - source_position;
9754 // Check whether we can't get any closer.
9755 if (distance == 0) break;
9756 }
9757 it.next();
9758 }
9759
9760 return Smi::FromInt(closest_pc);
9761}
9762
9763
ager@chromium.org357bf652010-04-12 11:30:10 +00009764// Calls specified function with or without entering the debugger.
9765// This is used in unit tests to run code as if debugger is entered or simply
9766// to have a stack with C++ frame in the middle.
9767static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9768 ASSERT(args.length() == 2);
9769 HandleScope scope;
9770 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9771 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9772
9773 Handle<Object> result;
9774 bool pending_exception;
9775 {
9776 if (without_debugger) {
9777 result = Execution::Call(function, Top::global(), 0, NULL,
9778 &pending_exception);
9779 } else {
9780 EnterDebugger enter_debugger;
9781 result = Execution::Call(function, Top::global(), 0, NULL,
9782 &pending_exception);
9783 }
9784 }
9785 if (!pending_exception) {
9786 return *result;
9787 } else {
9788 return Failure::Exception();
9789 }
9790}
9791
9792
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009793#endif // ENABLE_DEBUGGER_SUPPORT
9794
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009795#ifdef ENABLE_LOGGING_AND_PROFILING
9796
9797static Object* Runtime_ProfilerResume(Arguments args) {
9798 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009799 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009800
9801 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009802 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9803 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009804 return Heap::undefined_value();
9805}
9806
9807
9808static Object* Runtime_ProfilerPause(Arguments args) {
9809 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009810 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009811
9812 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009813 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9814 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009815 return Heap::undefined_value();
9816}
9817
9818#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009819
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009820// Finds the script object from the script data. NOTE: This operation uses
9821// heap traversal to find the function generated for the source position
9822// for the requested break point. For lazily compiled functions several heap
9823// traversals might be required rendering this operation as a rather slow
9824// operation. However for setting break points which is normally done through
9825// some kind of user interaction the performance is not crucial.
9826static Handle<Object> Runtime_GetScriptFromScriptName(
9827 Handle<String> script_name) {
9828 // Scan the heap for Script objects to find the script with the requested
9829 // script data.
9830 Handle<Script> script;
9831 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009832 HeapObject* obj = NULL;
9833 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009834 // If a script is found check if it has the script data requested.
9835 if (obj->IsScript()) {
9836 if (Script::cast(obj)->name()->IsString()) {
9837 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9838 script = Handle<Script>(Script::cast(obj));
9839 }
9840 }
9841 }
9842 }
9843
9844 // If no script with the requested script data is found return undefined.
9845 if (script.is_null()) return Factory::undefined_value();
9846
9847 // Return the script found.
9848 return GetScriptWrapper(script);
9849}
9850
9851
9852// Get the script object from script data. NOTE: Regarding performance
9853// see the NOTE for GetScriptFromScriptData.
9854// args[0]: script data for the script to find the source for
9855static Object* Runtime_GetScript(Arguments args) {
9856 HandleScope scope;
9857
9858 ASSERT(args.length() == 1);
9859
9860 CONVERT_CHECKED(String, script_name, args[0]);
9861
9862 // Find the requested script.
9863 Handle<Object> result =
9864 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
9865 return *result;
9866}
9867
9868
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009869// Determines whether the given stack frame should be displayed in
9870// a stack trace. The caller is the error constructor that asked
9871// for the stack trace to be collected. The first time a construct
9872// call to this function is encountered it is skipped. The seen_caller
9873// in/out parameter is used to remember if the caller has been seen
9874// yet.
9875static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
9876 bool* seen_caller) {
9877 // Only display JS frames.
9878 if (!raw_frame->is_java_script())
9879 return false;
9880 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
9881 Object* raw_fun = frame->function();
9882 // Not sure when this can happen but skip it just in case.
9883 if (!raw_fun->IsJSFunction())
9884 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009885 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009886 *seen_caller = true;
9887 return false;
9888 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009889 // Skip all frames until we've seen the caller. Also, skip the most
9890 // obvious builtin calls. Some builtin calls (such as Number.ADD
9891 // which is invoked using 'call') are very difficult to recognize
9892 // so we're leaving them in for now.
9893 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009894}
9895
9896
9897// Collect the raw data for a stack trace. Returns an array of three
9898// element segments each containing a receiver, function and native
9899// code offset.
9900static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009901 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009902 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009903 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
9904
9905 HandleScope scope;
9906
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009907 limit = Max(limit, 0); // Ensure that limit is not negative.
9908 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009909 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009910
9911 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009912 // If the caller parameter is a function we skip frames until we're
9913 // under it before starting to collect.
9914 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009915 int cursor = 0;
9916 int frames_seen = 0;
9917 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009918 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009919 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009920 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009921 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009922 Object* recv = frame->receiver();
9923 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009924 Address pc = frame->pc();
9925 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009926 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009927 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009928 if (cursor + 2 < elements->length()) {
9929 elements->set(cursor++, recv);
9930 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009931 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009932 } else {
9933 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009934 Handle<Object> recv_handle(recv);
9935 Handle<Object> fun_handle(fun);
9936 SetElement(result, cursor++, recv_handle);
9937 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009938 SetElement(result, cursor++, Handle<Smi>(offset));
9939 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009940 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009941 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009942 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009943
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009944 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009945 return *result;
9946}
9947
9948
ager@chromium.org3811b432009-10-28 14:53:37 +00009949// Returns V8 version as a string.
9950static Object* Runtime_GetV8Version(Arguments args) {
9951 ASSERT_EQ(args.length(), 0);
9952
9953 NoHandleAllocation ha;
9954
9955 const char* version_string = v8::V8::GetVersion();
9956
9957 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
9958}
9959
9960
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009961static Object* Runtime_Abort(Arguments args) {
9962 ASSERT(args.length() == 2);
9963 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
9964 Smi::cast(args[1])->value());
9965 Top::PrintStack();
9966 OS::Abort();
9967 UNREACHABLE();
9968 return NULL;
9969}
9970
9971
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009972static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
9973 ASSERT(args.length() == 0);
9974 HandleScope::DeleteExtensions();
9975 return Heap::undefined_value();
9976}
9977
9978
kasper.lund44510672008-07-25 07:37:58 +00009979#ifdef DEBUG
9980// ListNatives is ONLY used by the fuzz-natives.js in debug mode
9981// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009982static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009983 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009984 HandleScope scope;
9985 Handle<JSArray> result = Factory::NewJSArray(0);
9986 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009987 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +00009988#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009989 { \
9990 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009991 Handle<String> name; \
9992 /* Inline runtime functions have an underscore in front of the name. */ \
9993 if (inline_runtime_functions) { \
9994 name = Factory::NewStringFromAscii( \
9995 Vector<const char>("_" #Name, StrLength("_" #Name))); \
9996 } else { \
9997 name = Factory::NewStringFromAscii( \
9998 Vector<const char>(#Name, StrLength(#Name))); \
9999 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010000 Handle<JSArray> pair = Factory::NewJSArray(0); \
10001 SetElement(pair, 0, name); \
10002 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10003 SetElement(result, index++, pair); \
10004 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010005 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010006 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010007 inline_runtime_functions = true;
10008 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010009#undef ADD_ENTRY
10010 return *result;
10011}
kasper.lund44510672008-07-25 07:37:58 +000010012#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010013
10014
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010015static Object* Runtime_Log(Arguments args) {
10016 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010017 CONVERT_CHECKED(String, format, args[0]);
10018 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010019 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010020 Logger::LogRuntime(chars, elms);
10021 return Heap::undefined_value();
10022}
10023
10024
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010025static Object* Runtime_IS_VAR(Arguments args) {
10026 UNREACHABLE(); // implemented as macro in the parser
10027 return NULL;
10028}
10029
10030
10031// ----------------------------------------------------------------------------
10032// Implementation of Runtime
10033
ager@chromium.orga1645e22009-09-09 19:27:10 +000010034#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010035 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010036 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010037
10038static Runtime::Function Runtime_functions[] = {
10039 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010040 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010041};
10042
10043#undef F
10044
10045
10046Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10047 ASSERT(0 <= fid && fid < kNofFunctions);
10048 return &Runtime_functions[fid];
10049}
10050
10051
10052Runtime::Function* Runtime::FunctionForName(const char* name) {
10053 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10054 if (strcmp(f->name, name) == 0) {
10055 return f;
10056 }
10057 }
10058 return NULL;
10059}
10060
10061
10062void Runtime::PerformGC(Object* result) {
10063 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010064 if (failure->IsRetryAfterGC()) {
10065 // Try to do a garbage collection; ignore it if it fails. The C
10066 // entry stub will throw an out-of-memory exception in that case.
10067 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10068 } else {
10069 // Handle last resort GC and make sure to allow future allocations
10070 // to grow the heap without causing GCs (if possible).
10071 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010072 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010073 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010074}
10075
10076
10077} } // namespace v8::internal