blob: dcff28bc36a79bdf3745501b7a588aa89c06e288 [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"
35#include "compiler.h"
36#include "cpu.h"
37#include "dateparser.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"
42#include "platform.h"
43#include "runtime.h"
44#include "scopeinfo.h"
45#include "v8threads.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000046#include "smart-pointer.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000047#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048
kasperl@chromium.org71affb52009-05-26 05:44:31 +000049namespace v8 {
50namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051
52
ager@chromium.org3e875802009-06-29 08:26:34 +000053#define RUNTIME_ASSERT(value) \
54 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000055
56// Cast the given object to a value of the specified type and store
57// it in a variable with the given name. If the object is not of the
58// expected type call IllegalOperation and return.
59#define CONVERT_CHECKED(Type, name, obj) \
60 RUNTIME_ASSERT(obj->Is##Type()); \
61 Type* name = Type::cast(obj);
62
63#define CONVERT_ARG_CHECKED(Type, name, index) \
64 RUNTIME_ASSERT(args[index]->Is##Type()); \
65 Handle<Type> name = args.at<Type>(index);
66
kasper.lundbd3ec4e2008-07-09 11:06:54 +000067// Cast the given object to a boolean and store it in a variable with
68// the given name. If the object is not a boolean call IllegalOperation
69// and return.
70#define CONVERT_BOOLEAN_CHECKED(name, obj) \
71 RUNTIME_ASSERT(obj->IsBoolean()); \
72 bool name = (obj)->IsTrue();
73
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000074// Cast the given object to a Smi and store its value in an int variable
75// with the given name. If the object is not a Smi call IllegalOperation
76// and return.
77#define CONVERT_SMI_CHECKED(name, obj) \
78 RUNTIME_ASSERT(obj->IsSmi()); \
79 int name = Smi::cast(obj)->value();
80
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000081// Cast the given object to a double and store it in a variable with
82// the given name. If the object is not a number (as opposed to
83// the number not-a-number) call IllegalOperation and return.
84#define CONVERT_DOUBLE_CHECKED(name, obj) \
85 RUNTIME_ASSERT(obj->IsNumber()); \
86 double name = (obj)->Number();
87
88// Call the specified converter on the object *comand store the result in
89// a variable of the specified type with the given name. If the
90// object is not a Number call IllegalOperation and return.
91#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
92 RUNTIME_ASSERT(obj->IsNumber()); \
93 type name = NumberTo##Type(obj);
94
95// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000096static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097
98
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000099static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
100 StackLimitCheck check;
101 if (check.HasOverflowed()) return Top::StackOverflow();
102
103 Object* result = Heap::CopyJSObject(boilerplate);
104 if (result->IsFailure()) return result;
105 JSObject* copy = JSObject::cast(result);
106
107 // Deep copy local properties.
108 if (copy->HasFastProperties()) {
109 FixedArray* properties = copy->properties();
110 WriteBarrierMode mode = properties->GetWriteBarrierMode();
111 for (int i = 0; i < properties->length(); i++) {
112 Object* value = properties->get(i);
113 if (value->IsJSObject()) {
114 JSObject* jsObject = JSObject::cast(value);
115 result = DeepCopyBoilerplate(jsObject);
116 if (result->IsFailure()) return result;
117 properties->set(i, result, mode);
118 }
119 }
120 mode = copy->GetWriteBarrierMode();
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()) {
125 JSObject* jsObject = JSObject::cast(value);
126 result = DeepCopyBoilerplate(jsObject);
127 if (result->IsFailure()) return result;
128 copy->InObjectPropertyAtPut(i, result, mode);
129 }
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());
138 String* keyString = String::cast(names->get(i));
139 PropertyAttributes attributes =
140 copy->GetLocalPropertyAttribute(keyString);
141 // 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;
145 Object* value = copy->GetProperty(keyString, &attributes);
146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
148 JSObject* jsObject = JSObject::cast(value);
149 result = DeepCopyBoilerplate(jsObject);
150 if (result->IsFailure()) return result;
151 result = copy->SetProperty(keyString, result, NONE);
152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
158 if (copy->HasFastElements()) {
159 FixedArray* elements = copy->elements();
160 WriteBarrierMode mode = elements->GetWriteBarrierMode();
161 for (int i = 0; i < elements->length(); i++) {
162 Object* value = elements->get(i);
163 if (value->IsJSObject()) {
164 JSObject* jsObject = JSObject::cast(value);
165 result = DeepCopyBoilerplate(jsObject);
166 if (result->IsFailure()) return result;
167 elements->set(i, result, mode);
168 }
169 }
170 } else {
171 Dictionary* element_dictionary = copy->element_dictionary();
172 int capacity = element_dictionary->Capacity();
173 for (int i = 0; i < capacity; i++) {
174 Object* k = element_dictionary->KeyAt(i);
175 if (element_dictionary->IsKey(k)) {
176 Object* value = element_dictionary->ValueAt(i);
177 if (value->IsJSObject()) {
178 JSObject* jsObject = JSObject::cast(value);
179 result = DeepCopyBoilerplate(jsObject);
180 if (result->IsFailure()) return result;
181 element_dictionary->ValueAtPut(i, result);
182 }
183 }
184 }
185 }
186 return copy;
187}
188
189
190static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
191 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
192 return DeepCopyBoilerplate(boilerplate);
193}
194
195
196static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000197 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000198 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000199}
200
201
ager@chromium.org236ad962008-09-25 09:45:57 +0000202static Handle<Map> ComputeObjectLiteralMap(
203 Handle<Context> context,
204 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000205 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000206 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000207 if (FLAG_canonicalize_object_literal_maps) {
208 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000209 int number_of_symbol_keys = 0;
210 while ((number_of_symbol_keys < number_of_properties) &&
211 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
212 number_of_symbol_keys++;
213 }
214 // Based on the number of prefix symbols key we decide whether
215 // to use the map cache in the global context.
216 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000217 if ((number_of_symbol_keys == number_of_properties) &&
218 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000219 // Create the fixed array with the key.
220 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
221 for (int i = 0; i < number_of_symbol_keys; i++) {
222 keys->set(i, constant_properties->get(i*2));
223 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000224 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000225 return Factory::ObjectLiteralMapFromCache(context, keys);
226 }
227 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000228 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000229 return Factory::CopyMap(
230 Handle<Map>(context->object_function()->initial_map()),
231 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000232}
233
234
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000235static Handle<Object> CreateLiteralBoilerplate(
236 Handle<FixedArray> literals,
237 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000238
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000239
240static Handle<Object> CreateObjectLiteralBoilerplate(
241 Handle<FixedArray> literals,
242 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000243 // Get the global context from the literals array. This is the
244 // context in which the function was created and we use the object
245 // function from this context to create the object literal. We do
246 // not use the object function from the current global context
247 // because this might be the object function from another context
248 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000249 Handle<Context> context =
250 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
251
252 bool is_result_from_cache;
253 Handle<Map> map = ComputeObjectLiteralMap(context,
254 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000255 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000256
ager@chromium.org236ad962008-09-25 09:45:57 +0000257 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000258 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000259 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000260 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
261 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000262 for (int index = 0; index < length; index +=2) {
263 Handle<Object> key(constant_properties->get(index+0));
264 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000265 if (value->IsFixedArray()) {
266 // The value contains the constant_properties of a
267 // simple object literal.
268 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
269 value = CreateLiteralBoilerplate(literals, array);
270 if (value.is_null()) return value;
271 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000272 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 uint32_t element_index = 0;
274 if (key->IsSymbol()) {
275 // If key is a symbol it is not an array element.
276 Handle<String> name(String::cast(*key));
277 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000278 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 } else if (Array::IndexFromObject(*key, &element_index)) {
280 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000281 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000282 } else {
283 // Non-uint32 number.
284 ASSERT(key->IsNumber());
285 double num = key->Number();
286 char arr[100];
287 Vector<char> buffer(arr, ARRAY_SIZE(arr));
288 const char* str = DoubleToCString(num, buffer);
289 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000290 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000292 // If setting the property on the boilerplate throws an
293 // exception, the exception is converted to an empty handle in
294 // the handle based operations. In that case, we need to
295 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000296 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 }
298 }
299
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000300 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000301}
302
303
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000304static Handle<Object> CreateArrayLiteralBoilerplate(
305 Handle<FixedArray> literals,
306 Handle<FixedArray> elements) {
307 // Create the JSArray.
308 Handle<JSFunction> constructor(
309 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
310 Handle<Object> object = Factory::NewJSObject(constructor);
311
312 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
313
314 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
315 for (int i = 0; i < content->length(); i++) {
316 if (content->get(i)->IsFixedArray()) {
317 // The value contains the constant_properties of a
318 // simple object literal.
319 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
320 Handle<Object> result =
321 CreateLiteralBoilerplate(literals, fa);
322 if (result.is_null()) return result;
323 content->set(i, *result);
324 }
325 }
326
327 // Set the elements.
328 Handle<JSArray>::cast(object)->SetContent(*content);
329 return object;
330}
331
332
333static Handle<Object> CreateLiteralBoilerplate(
334 Handle<FixedArray> literals,
335 Handle<FixedArray> array) {
336 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
337 switch (CompileTimeValue::GetType(array)) {
338 case CompileTimeValue::OBJECT_LITERAL:
339 return CreateObjectLiteralBoilerplate(literals, elements);
340 case CompileTimeValue::ARRAY_LITERAL:
341 return CreateArrayLiteralBoilerplate(literals, elements);
342 default:
343 UNREACHABLE();
344 return Handle<Object>::null();
345 }
346}
347
348
349static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
350 HandleScope scope;
351 ASSERT(args.length() == 3);
352 // Copy the arguments.
353 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
354 CONVERT_SMI_CHECKED(literals_index, args[1]);
355 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
356
357 Handle<Object> result =
358 CreateObjectLiteralBoilerplate(literals, constant_properties);
359
360 if (result.is_null()) return Failure::Exception();
361
362 // Update the functions literal and return the boilerplate.
363 literals->set(literals_index, *result);
364
365 return *result;
366}
367
368
369static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000370 // Takes a FixedArray of elements containing the literal elements of
371 // the array literal and produces JSArray with those elements.
372 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000373 // which contains the context from which to get the Array function
374 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000375 HandleScope scope;
376 ASSERT(args.length() == 3);
377 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
378 CONVERT_SMI_CHECKED(literals_index, args[1]);
379 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
382 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000383
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000384 // Update the functions literal and return the boilerplate.
385 literals->set(literals_index, *object);
386 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000387}
388
389
ager@chromium.org32912102009-01-16 10:38:43 +0000390static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
391 ASSERT(args.length() == 2);
392 CONVERT_CHECKED(String, key, args[0]);
393 Object* value = args[1];
394 // Create a catch context extension object.
395 JSFunction* constructor =
396 Top::context()->global_context()->context_extension_function();
397 Object* object = Heap::AllocateJSObject(constructor);
398 if (object->IsFailure()) return object;
399 // Assign the exception value to the catch variable and make sure
400 // that the catch variable is DontDelete.
401 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
402 if (value->IsFailure()) return value;
403 return object;
404}
405
406
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000407static Object* Runtime_ClassOf(Arguments args) {
408 NoHandleAllocation ha;
409 ASSERT(args.length() == 1);
410 Object* obj = args[0];
411 if (!obj->IsJSObject()) return Heap::null_value();
412 return JSObject::cast(obj)->class_name();
413}
414
ager@chromium.org7c537e22008-10-16 08:43:32 +0000415
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000416static Object* Runtime_HasStringClass(Arguments args) {
417 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000418}
419
420
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000421static Object* Runtime_HasDateClass(Arguments args) {
422 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000423}
424
425
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000426static Object* Runtime_HasArrayClass(Arguments args) {
427 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
428}
429
430
431static Object* Runtime_HasFunctionClass(Arguments args) {
432 return Heap::ToBoolean(
433 args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
434}
435
436
437static Object* Runtime_HasNumberClass(Arguments args) {
438 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
439}
440
441
442static Object* Runtime_HasBooleanClass(Arguments args) {
443 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
444}
445
446
447static Object* Runtime_HasArgumentsClass(Arguments args) {
448 return Heap::ToBoolean(
449 args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
450}
451
452
453static Object* Runtime_HasRegExpClass(Arguments args) {
454 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000455}
456
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000457
458static Object* Runtime_IsInPrototypeChain(Arguments args) {
459 NoHandleAllocation ha;
460 ASSERT(args.length() == 2);
461 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
462 Object* O = args[0];
463 Object* V = args[1];
464 while (true) {
465 Object* prototype = V->GetPrototype();
466 if (prototype->IsNull()) return Heap::false_value();
467 if (O == prototype) return Heap::true_value();
468 V = prototype;
469 }
470}
471
472
ager@chromium.org9085a012009-05-11 19:22:57 +0000473// Inserts an object as the hidden prototype of another object.
474static Object* Runtime_SetHiddenPrototype(Arguments args) {
475 NoHandleAllocation ha;
476 ASSERT(args.length() == 2);
477 CONVERT_CHECKED(JSObject, jsobject, args[0]);
478 CONVERT_CHECKED(JSObject, proto, args[1]);
479
480 // Sanity checks. The old prototype (that we are replacing) could
481 // theoretically be null, but if it is not null then check that we
482 // didn't already install a hidden prototype here.
483 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
484 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
485 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
486
487 // Allocate up front before we start altering state in case we get a GC.
488 Object* map_or_failure = proto->map()->CopyDropTransitions();
489 if (map_or_failure->IsFailure()) return map_or_failure;
490 Map* new_proto_map = Map::cast(map_or_failure);
491
492 map_or_failure = jsobject->map()->CopyDropTransitions();
493 if (map_or_failure->IsFailure()) return map_or_failure;
494 Map* new_map = Map::cast(map_or_failure);
495
496 // Set proto's prototype to be the old prototype of the object.
497 new_proto_map->set_prototype(jsobject->GetPrototype());
498 proto->set_map(new_proto_map);
499 new_proto_map->set_is_hidden_prototype();
500
501 // Set the object's prototype to proto.
502 new_map->set_prototype(proto);
503 jsobject->set_map(new_map);
504
505 return Heap::undefined_value();
506}
507
508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000509static Object* Runtime_IsConstructCall(Arguments args) {
510 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000511 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000512 JavaScriptFrameIterator it;
513 return Heap::ToBoolean(it.frame()->IsConstructor());
514}
515
516
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000517static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000518 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000519 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000520 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
521 CONVERT_ARG_CHECKED(String, pattern, 1);
522 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000523 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
524 if (result.is_null()) return Failure::Exception();
525 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000526}
527
528
529static Object* Runtime_CreateApiFunction(Arguments args) {
530 HandleScope scope;
531 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000532 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000533 return *Factory::CreateApiFunction(data);
534}
535
536
537static Object* Runtime_IsTemplate(Arguments args) {
538 ASSERT(args.length() == 1);
539 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000540 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000541 return Heap::ToBoolean(result);
542}
543
544
545static Object* Runtime_GetTemplateField(Arguments args) {
546 ASSERT(args.length() == 2);
547 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000549 int index = field->value();
550 int offset = index * kPointerSize + HeapObject::kHeaderSize;
551 InstanceType type = templ->map()->instance_type();
552 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
553 type == OBJECT_TEMPLATE_INFO_TYPE);
554 RUNTIME_ASSERT(offset > 0);
555 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
556 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
557 } else {
558 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
559 }
560 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561}
562
563
ager@chromium.org870a0b62008-11-04 11:43:05 +0000564static Object* Runtime_DisableAccessChecks(Arguments args) {
565 ASSERT(args.length() == 1);
566 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000567 Map* old_map = object->map();
568 bool needs_access_checks = old_map->is_access_check_needed();
569 if (needs_access_checks) {
570 // Copy map so it won't interfere constructor's initial map.
571 Object* new_map = old_map->CopyDropTransitions();
572 if (new_map->IsFailure()) return new_map;
573
574 Map::cast(new_map)->set_is_access_check_needed(false);
575 object->set_map(Map::cast(new_map));
576 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000577 return needs_access_checks ? Heap::true_value() : Heap::false_value();
578}
579
580
581static Object* Runtime_EnableAccessChecks(Arguments args) {
582 ASSERT(args.length() == 1);
583 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000584 Map* old_map = object->map();
585 if (!old_map->is_access_check_needed()) {
586 // Copy map so it won't interfere constructor's initial map.
587 Object* new_map = old_map->CopyDropTransitions();
588 if (new_map->IsFailure()) return new_map;
589
590 Map::cast(new_map)->set_is_access_check_needed(true);
591 object->set_map(Map::cast(new_map));
592 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000593 return Heap::undefined_value();
594}
595
596
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000597static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
598 HandleScope scope;
599 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
600 Handle<Object> args[2] = { type_handle, name };
601 Handle<Object> error =
602 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
603 return Top::Throw(*error);
604}
605
606
607static Object* Runtime_DeclareGlobals(Arguments args) {
608 HandleScope scope;
609 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
610
611 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
612 Handle<Context> context = args.at<Context>(1);
613 bool is_eval = Smi::cast(args[2])->value() == 1;
614
615 // Compute the property attributes. According to ECMA-262, section
616 // 13, page 71, the property must be read-only and
617 // non-deletable. However, neither SpiderMonkey nor KJS creates the
618 // property as read-only, so we don't either.
619 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
620
621 // Only optimize the object if we intend to add more than 5 properties.
622 OptimizedObjectForAddingMultipleProperties ba(global, pairs->length()/2 > 5);
623
624 // Traverse the name/value pairs and set the properties.
625 int length = pairs->length();
626 for (int i = 0; i < length; i += 2) {
627 HandleScope scope;
628 Handle<String> name(String::cast(pairs->get(i)));
629 Handle<Object> value(pairs->get(i + 1));
630
631 // We have to declare a global const property. To capture we only
632 // assign to it when evaluating the assignment for "const x =
633 // <expr>" the initial value is the hole.
634 bool is_const_property = value->IsTheHole();
635
636 if (value->IsUndefined() || is_const_property) {
637 // Lookup the property in the global object, and don't set the
638 // value of the variable if the property is already there.
639 LookupResult lookup;
640 global->Lookup(*name, &lookup);
641 if (lookup.IsProperty()) {
642 // Determine if the property is local by comparing the holder
643 // against the global object. The information will be used to
644 // avoid throwing re-declaration errors when declaring
645 // variables or constants that exist in the prototype chain.
646 bool is_local = (*global == lookup.holder());
647 // Get the property attributes and determine if the property is
648 // read-only.
649 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
650 bool is_read_only = (attributes & READ_ONLY) != 0;
651 if (lookup.type() == INTERCEPTOR) {
652 // If the interceptor says the property is there, we
653 // just return undefined without overwriting the property.
654 // Otherwise, we continue to setting the property.
655 if (attributes != ABSENT) {
656 // Check if the existing property conflicts with regards to const.
657 if (is_local && (is_read_only || is_const_property)) {
658 const char* type = (is_read_only) ? "const" : "var";
659 return ThrowRedeclarationError(type, name);
660 };
661 // The property already exists without conflicting: Go to
662 // the next declaration.
663 continue;
664 }
665 // Fall-through and introduce the absent property by using
666 // SetProperty.
667 } else {
668 if (is_local && (is_read_only || is_const_property)) {
669 const char* type = (is_read_only) ? "const" : "var";
670 return ThrowRedeclarationError(type, name);
671 }
672 // The property already exists without conflicting: Go to
673 // the next declaration.
674 continue;
675 }
676 }
677 } else {
678 // Copy the function and update its context. Use it as value.
679 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
680 Handle<JSFunction> function =
681 Factory::NewFunctionFromBoilerplate(boilerplate, context);
682 value = function;
683 }
684
685 LookupResult lookup;
686 global->LocalLookup(*name, &lookup);
687
688 PropertyAttributes attributes = is_const_property
689 ? static_cast<PropertyAttributes>(base | READ_ONLY)
690 : base;
691
692 if (lookup.IsProperty()) {
693 // There's a local property that we need to overwrite because
694 // we're either declaring a function or there's an interceptor
695 // that claims the property is absent.
696
697 // Check for conflicting re-declarations. We cannot have
698 // conflicting types in case of intercepted properties because
699 // they are absent.
700 if (lookup.type() != INTERCEPTOR &&
701 (lookup.IsReadOnly() || is_const_property)) {
702 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
703 return ThrowRedeclarationError(type, name);
704 }
705 SetProperty(global, name, value, attributes);
706 } else {
707 // If a property with this name does not already exist on the
708 // global object add the property locally. We take special
709 // precautions to always add it as a local property even in case
710 // of callbacks in the prototype chain (this rules out using
711 // SetProperty). Also, we must use the handle-based version to
712 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000713 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 }
715 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000716
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000717 return Heap::undefined_value();
718}
719
720
721static Object* Runtime_DeclareContextSlot(Arguments args) {
722 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000723 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724
ager@chromium.org7c537e22008-10-16 08:43:32 +0000725 CONVERT_ARG_CHECKED(Context, context, 0);
726 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000727 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000728 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000729 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000730 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000731
732 // Declarations are always done in the function context.
733 context = Handle<Context>(context->fcontext());
734
735 int index;
736 PropertyAttributes attributes;
737 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000738 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739 context->Lookup(name, flags, &index, &attributes);
740
741 if (attributes != ABSENT) {
742 // The name was declared before; check for conflicting
743 // re-declarations: This is similar to the code in parser.cc in
744 // the AstBuildingParser::Declare function.
745 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
746 // Functions are not read-only.
747 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
748 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
749 return ThrowRedeclarationError(type, name);
750 }
751
752 // Initialize it if necessary.
753 if (*initial_value != NULL) {
754 if (index >= 0) {
755 // The variable or constant context slot should always be in
756 // the function context; not in any outer context nor in the
757 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000758 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000759 if (((attributes & READ_ONLY) == 0) ||
760 context->get(index)->IsTheHole()) {
761 context->set(index, *initial_value);
762 }
763 } else {
764 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000765 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766 SetProperty(context_ext, name, initial_value, mode);
767 }
768 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000770 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000771 // The property is not in the function context. It needs to be
772 // "declared" in the function context's extension context, or in the
773 // global context.
774 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000775 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000776 // The function context's extension context exists - use it.
777 context_ext = Handle<JSObject>(context->extension());
778 } else {
779 // The function context's extension context does not exists - allocate
780 // it.
781 context_ext = Factory::NewJSObject(Top::context_extension_function());
782 // And store it in the extension slot.
783 context->set_extension(*context_ext);
784 }
785 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000786
ager@chromium.org7c537e22008-10-16 08:43:32 +0000787 // Declare the property by setting it to the initial value if provided,
788 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
789 // constant declarations).
790 ASSERT(!context_ext->HasLocalProperty(*name));
791 Handle<Object> value(Heap::undefined_value());
792 if (*initial_value != NULL) value = initial_value;
793 SetProperty(context_ext, name, value, mode);
794 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
795 }
796
797 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000798}
799
800
801static Object* Runtime_InitializeVarGlobal(Arguments args) {
802 NoHandleAllocation nha;
803
804 // Determine if we need to assign to the variable if it already
805 // exists (based on the number of arguments).
806 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
807 bool assign = args.length() == 2;
808
809 CONVERT_ARG_CHECKED(String, name, 0);
810 GlobalObject* global = Top::context()->global();
811
812 // According to ECMA-262, section 12.2, page 62, the property must
813 // not be deletable.
814 PropertyAttributes attributes = DONT_DELETE;
815
816 // Lookup the property locally in the global object. If it isn't
817 // there, we add the property and take special precautions to always
818 // add it as a local property even in case of callbacks in the
819 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000820 // We have IgnoreAttributesAndSetLocalProperty for this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000821 LookupResult lookup;
822 global->LocalLookup(*name, &lookup);
823 if (!lookup.IsProperty()) {
824 Object* value = (assign) ? args[1] : Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000825 return global->IgnoreAttributesAndSetLocalProperty(*name,
826 value,
827 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000828 }
829
830 // Determine if this is a redeclaration of something read-only.
831 if (lookup.IsReadOnly()) {
832 return ThrowRedeclarationError("const", name);
833 }
834
835 // Determine if this is a redeclaration of an intercepted read-only
836 // property and figure out if the property exists at all.
837 bool found = true;
838 PropertyType type = lookup.type();
839 if (type == INTERCEPTOR) {
840 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
841 if (intercepted == ABSENT) {
842 // The interceptor claims the property isn't there. We need to
843 // make sure to introduce it.
844 found = false;
845 } else if ((intercepted & READ_ONLY) != 0) {
846 // The property is present, but read-only. Since we're trying to
847 // overwrite it with a variable declaration we must throw a
848 // re-declaration error.
849 return ThrowRedeclarationError("const", name);
850 }
851 // Restore global object from context (in case of GC).
852 global = Top::context()->global();
853 }
854
855 if (found && !assign) {
856 // The global property is there and we're not assigning any value
857 // to it. Just return.
858 return Heap::undefined_value();
859 }
860
861 // Assign the value (or undefined) to the property.
862 Object* value = (assign) ? args[1] : Heap::undefined_value();
863 return global->SetProperty(&lookup, *name, value, attributes);
864}
865
866
867static Object* Runtime_InitializeConstGlobal(Arguments args) {
868 // All constants are declared with an initial value. The name
869 // of the constant is the first argument and the initial value
870 // is the second.
871 RUNTIME_ASSERT(args.length() == 2);
872 CONVERT_ARG_CHECKED(String, name, 0);
873 Handle<Object> value = args.at<Object>(1);
874
875 // Get the current global object from top.
876 GlobalObject* global = Top::context()->global();
877
878 // According to ECMA-262, section 12.2, page 62, the property must
879 // not be deletable. Since it's a const, it must be READ_ONLY too.
880 PropertyAttributes attributes =
881 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
882
883 // Lookup the property locally in the global object. If it isn't
884 // there, we add the property and take special precautions to always
885 // add it as a local property even in case of callbacks in the
886 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000887 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000888 LookupResult lookup;
889 global->LocalLookup(*name, &lookup);
890 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000891 return global->IgnoreAttributesAndSetLocalProperty(*name,
892 *value,
893 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894 }
895
896 // Determine if this is a redeclaration of something not
897 // read-only. In case the result is hidden behind an interceptor we
898 // need to ask it for the property attributes.
899 if (!lookup.IsReadOnly()) {
900 if (lookup.type() != INTERCEPTOR) {
901 return ThrowRedeclarationError("var", name);
902 }
903
904 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
905
906 // Throw re-declaration error if the intercepted property is present
907 // but not read-only.
908 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
909 return ThrowRedeclarationError("var", name);
910 }
911
912 // Restore global object from context (in case of GC) and continue
913 // with setting the value because the property is either absent or
914 // read-only. We also have to do redo the lookup.
915 global = Top::context()->global();
916
917 // BUG 1213579: Handle the case where we have to set a read-only
918 // property through an interceptor and only do it if it's
919 // uninitialized, e.g. the hole. Nirk...
920 global->SetProperty(*name, *value, attributes);
921 return *value;
922 }
923
924 // Set the value, but only we're assigning the initial value to a
925 // constant. For now, we determine this by checking if the
926 // current value is the hole.
927 PropertyType type = lookup.type();
928 if (type == FIELD) {
929 FixedArray* properties = global->properties();
930 int index = lookup.GetFieldIndex();
931 if (properties->get(index)->IsTheHole()) {
932 properties->set(index, *value);
933 }
934 } else if (type == NORMAL) {
935 Dictionary* dictionary = global->property_dictionary();
936 int entry = lookup.GetDictionaryEntry();
937 if (dictionary->ValueAt(entry)->IsTheHole()) {
938 dictionary->ValueAtPut(entry, *value);
939 }
940 } else {
941 // Ignore re-initialization of constants that have already been
942 // assigned a function value.
943 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
944 }
945
946 // Use the set value as the result of the operation.
947 return *value;
948}
949
950
951static Object* Runtime_InitializeConstContextSlot(Arguments args) {
952 HandleScope scope;
953 ASSERT(args.length() == 3);
954
955 Handle<Object> value(args[0]);
956 ASSERT(!value->IsTheHole());
957 CONVERT_ARG_CHECKED(Context, context, 1);
958 Handle<String> name(String::cast(args[2]));
959
960 // Initializations are always done in the function context.
961 context = Handle<Context>(context->fcontext());
962
963 int index;
964 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000965 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000966 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000967 context->Lookup(name, flags, &index, &attributes);
968
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000969 // In most situations, the property introduced by the const
970 // declaration should be present in the context extension object.
971 // However, because declaration and initialization are separate, the
972 // property might have been deleted (if it was introduced by eval)
973 // before we reach the initialization point.
974 //
975 // Example:
976 //
977 // function f() { eval("delete x; const x;"); }
978 //
979 // In that case, the initialization behaves like a normal assignment
980 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000981 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000982 // Property was found in a context.
983 if (holder->IsContext()) {
984 // The holder cannot be the function context. If it is, there
985 // should have been a const redeclaration error when declaring
986 // the const property.
987 ASSERT(!holder.is_identical_to(context));
988 if ((attributes & READ_ONLY) == 0) {
989 Handle<Context>::cast(holder)->set(index, *value);
990 }
991 } else {
992 // The holder is an arguments object.
993 ASSERT((attributes & READ_ONLY) == 0);
994 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000995 }
996 return *value;
997 }
998
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000999 // The property could not be found, we introduce it in the global
1000 // context.
1001 if (attributes == ABSENT) {
1002 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1003 SetProperty(global, name, value, NONE);
1004 return *value;
1005 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001006
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001007 // The property was present in a context extension object.
1008 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001009
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001010 if (*context_ext == context->extension()) {
1011 // This is the property that was introduced by the const
1012 // declaration. Set it if it hasn't been set before. NOTE: We
1013 // cannot use GetProperty() to get the current value as it
1014 // 'unholes' the value.
1015 LookupResult lookup;
1016 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1017 ASSERT(lookup.IsProperty()); // the property was declared
1018 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1019
1020 PropertyType type = lookup.type();
1021 if (type == FIELD) {
1022 FixedArray* properties = context_ext->properties();
1023 int index = lookup.GetFieldIndex();
1024 if (properties->get(index)->IsTheHole()) {
1025 properties->set(index, *value);
1026 }
1027 } else if (type == NORMAL) {
1028 Dictionary* dictionary = context_ext->property_dictionary();
1029 int entry = lookup.GetDictionaryEntry();
1030 if (dictionary->ValueAt(entry)->IsTheHole()) {
1031 dictionary->ValueAtPut(entry, *value);
1032 }
1033 } else {
1034 // We should not reach here. Any real, named property should be
1035 // either a field or a dictionary slot.
1036 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 }
1038 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001039 // The property was found in a different context extension object.
1040 // Set it if it is not a read-only property.
1041 if ((attributes & READ_ONLY) == 0) {
1042 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1043 // Setting a property might throw an exception. Exceptions
1044 // are converted to empty handles in handle operations. We
1045 // need to convert back to exceptions here.
1046 if (set.is_null()) {
1047 ASSERT(Top::has_pending_exception());
1048 return Failure::Exception();
1049 }
1050 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001051 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001052
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001053 return *value;
1054}
1055
1056
1057static Object* Runtime_RegExpExec(Arguments args) {
1058 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001059 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001060 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1061 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001062 // Due to the way the JS files are constructed this must be less than the
1063 // length of a string, i.e. it is always a Smi. We check anyway for security.
1064 CONVERT_CHECKED(Smi, index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001065 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001066 RUNTIME_ASSERT(last_match_info->HasFastElements());
1067 RUNTIME_ASSERT(index->value() >= 0);
1068 RUNTIME_ASSERT(index->value() <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001069 Handle<Object> result = RegExpImpl::Exec(regexp,
1070 subject,
1071 index->value(),
1072 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001073 if (result.is_null()) return Failure::Exception();
1074 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001075}
1076
1077
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001078static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1079 HandleScope scope;
1080 ASSERT(args.length() == 4);
1081 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1082 int index = Smi::cast(args[1])->value();
1083 Handle<String> pattern = args.at<String>(2);
1084 Handle<String> flags = args.at<String>(3);
1085
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001086 // Get the RegExp function from the context in the literals array.
1087 // This is the RegExp function from the context in which the
1088 // function was created. We do not use the RegExp function from the
1089 // current global context because this might be the RegExp function
1090 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001091 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001092 Handle<JSFunction>(
1093 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 // Compute the regular expression literal.
1095 bool has_pending_exception;
1096 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001097 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1098 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001099 if (has_pending_exception) {
1100 ASSERT(Top::has_pending_exception());
1101 return Failure::Exception();
1102 }
1103 literals->set(index, *regexp);
1104 return *regexp;
1105}
1106
1107
1108static Object* Runtime_FunctionGetName(Arguments args) {
1109 NoHandleAllocation ha;
1110 ASSERT(args.length() == 1);
1111
1112 CONVERT_CHECKED(JSFunction, f, args[0]);
1113 return f->shared()->name();
1114}
1115
1116
ager@chromium.org236ad962008-09-25 09:45:57 +00001117static Object* Runtime_FunctionSetName(Arguments args) {
1118 NoHandleAllocation ha;
1119 ASSERT(args.length() == 2);
1120
1121 CONVERT_CHECKED(JSFunction, f, args[0]);
1122 CONVERT_CHECKED(String, name, args[1]);
1123 f->shared()->set_name(name);
1124 return Heap::undefined_value();
1125}
1126
1127
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001128static Object* Runtime_FunctionGetScript(Arguments args) {
1129 HandleScope scope;
1130 ASSERT(args.length() == 1);
1131
1132 CONVERT_CHECKED(JSFunction, fun, args[0]);
1133 Handle<Object> script = Handle<Object>(fun->shared()->script());
1134 if (!script->IsScript()) return Heap::undefined_value();
1135
1136 return *GetScriptWrapper(Handle<Script>::cast(script));
1137}
1138
1139
1140static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1141 NoHandleAllocation ha;
1142 ASSERT(args.length() == 1);
1143
1144 CONVERT_CHECKED(JSFunction, f, args[0]);
1145 return f->shared()->GetSourceCode();
1146}
1147
1148
1149static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1150 NoHandleAllocation ha;
1151 ASSERT(args.length() == 1);
1152
1153 CONVERT_CHECKED(JSFunction, fun, args[0]);
1154 int pos = fun->shared()->start_position();
1155 return Smi::FromInt(pos);
1156}
1157
1158
1159static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1160 NoHandleAllocation ha;
1161 ASSERT(args.length() == 2);
1162
1163 CONVERT_CHECKED(JSFunction, fun, args[0]);
1164 CONVERT_CHECKED(String, name, args[1]);
1165 fun->SetInstanceClassName(name);
1166 return Heap::undefined_value();
1167}
1168
1169
1170static Object* Runtime_FunctionSetLength(Arguments args) {
1171 NoHandleAllocation ha;
1172 ASSERT(args.length() == 2);
1173
1174 CONVERT_CHECKED(JSFunction, fun, args[0]);
1175 CONVERT_CHECKED(Smi, length, args[1]);
1176 fun->shared()->set_length(length->value());
1177 return length;
1178}
1179
1180
1181static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001182 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001183 ASSERT(args.length() == 2);
1184
1185 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001186 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1187 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001188 return args[0]; // return TOS
1189}
1190
1191
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001192static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1193 NoHandleAllocation ha;
1194 ASSERT(args.length() == 1);
1195
1196 CONVERT_CHECKED(JSFunction, f, args[0]);
1197 // The function_data field of the shared function info is used exclusively by
1198 // the API.
1199 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1200 : Heap::false_value();
1201}
1202
1203
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001204static Object* Runtime_SetCode(Arguments args) {
1205 HandleScope scope;
1206 ASSERT(args.length() == 2);
1207
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001208 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209 Handle<Object> code = args.at<Object>(1);
1210
1211 Handle<Context> context(target->context());
1212
1213 if (!code->IsNull()) {
1214 RUNTIME_ASSERT(code->IsJSFunction());
1215 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1216 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1217 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1218 return Failure::Exception();
1219 }
1220 // Set the code, formal parameter count, and the length of the target
1221 // function.
1222 target->set_code(fun->code());
1223 target->shared()->set_length(fun->shared()->length());
1224 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001225 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001226 // Set the source code of the target function to undefined.
1227 // SetCode is only used for built-in constructors like String,
1228 // Array, and Object, and some web code
1229 // doesn't like seeing source code for constructors.
1230 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001231 context = Handle<Context>(fun->context());
1232
1233 // Make sure we get a fresh copy of the literal vector to avoid
1234 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001235 int number_of_literals = fun->NumberOfLiterals();
1236 Handle<FixedArray> literals =
1237 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001238 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001239 // Insert the object, regexp and array functions in the literals
1240 // array prefix. These are the functions that will be used when
1241 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001242 literals->set(JSFunction::kLiteralGlobalContextIndex,
1243 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001245 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001246 }
1247
1248 target->set_context(*context);
1249 return *target;
1250}
1251
1252
1253static Object* CharCodeAt(String* subject, Object* index) {
1254 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001255 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001256 // Flatten the string. If someone wants to get a char at an index
1257 // in a cons string, it is likely that more indices will be
1258 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001259 subject->TryFlattenIfNotFlat();
1260 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001261 return Heap::nan_value();
1262 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001263 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001264}
1265
1266
1267static Object* Runtime_StringCharCodeAt(Arguments args) {
1268 NoHandleAllocation ha;
1269 ASSERT(args.length() == 2);
1270
1271 CONVERT_CHECKED(String, subject, args[0]);
1272 Object* index = args[1];
1273 return CharCodeAt(subject, index);
1274}
1275
1276
1277static Object* Runtime_CharFromCode(Arguments args) {
1278 NoHandleAllocation ha;
1279 ASSERT(args.length() == 1);
1280 uint32_t code;
1281 if (Array::IndexFromObject(args[0], &code)) {
1282 if (code <= 0xffff) {
1283 return Heap::LookupSingleCharacterStringFromCode(code);
1284 }
1285 }
1286 return Heap::empty_string();
1287}
1288
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001289// Forward declarations.
1290static const int kStringBuilderConcatHelperLengthBits = 11;
1291static const int kStringBuilderConcatHelperPositionBits = 19;
1292
1293template <typename schar>
1294static inline void StringBuilderConcatHelper(String*,
1295 schar*,
1296 FixedArray*,
1297 int);
1298
1299typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1300typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1301
1302class ReplacementStringBuilder {
1303 public:
1304 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1305 : subject_(subject),
1306 parts_(Factory::NewFixedArray(estimated_part_count)),
1307 part_count_(0),
1308 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001309 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001310 // Require a non-zero initial size. Ensures that doubling the size to
1311 // extend the array will work.
1312 ASSERT(estimated_part_count > 0);
1313 }
1314
1315 void EnsureCapacity(int elements) {
1316 int length = parts_->length();
1317 int required_length = part_count_ + elements;
1318 if (length < required_length) {
1319 int new_length = length;
1320 do {
1321 new_length *= 2;
1322 } while (new_length < required_length);
1323 Handle<FixedArray> extended_array =
1324 Factory::NewFixedArray(new_length);
1325 parts_->CopyTo(0, *extended_array, 0, part_count_);
1326 parts_ = extended_array;
1327 }
1328 }
1329
1330 void AddSubjectSlice(int from, int to) {
1331 ASSERT(from >= 0);
1332 int length = to - from;
1333 ASSERT(length > 0);
1334 // Can we encode the slice in 11 bits for length and 19 bits for
1335 // start position - as used by StringBuilderConcatHelper?
1336 if (StringBuilderSubstringLength::is_valid(length) &&
1337 StringBuilderSubstringPosition::is_valid(from)) {
1338 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1339 StringBuilderSubstringPosition::encode(from);
1340 AddElement(Smi::FromInt(encoded_slice));
1341 } else {
1342 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1343 AddElement(*slice);
1344 }
1345 IncrementCharacterCount(length);
1346 }
1347
1348
1349 void AddString(Handle<String> string) {
1350 int length = string->length();
1351 ASSERT(length > 0);
1352 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001353 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001354 is_ascii_ = false;
1355 }
1356 IncrementCharacterCount(length);
1357 }
1358
1359
1360 Handle<String> ToString() {
1361 if (part_count_ == 0) {
1362 return Factory::empty_string();
1363 }
1364
1365 Handle<String> joined_string;
1366 if (is_ascii_) {
1367 joined_string = NewRawAsciiString(character_count_);
1368 AssertNoAllocation no_alloc;
1369 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1370 char* char_buffer = seq->GetChars();
1371 StringBuilderConcatHelper(*subject_,
1372 char_buffer,
1373 *parts_,
1374 part_count_);
1375 } else {
1376 // Non-ASCII.
1377 joined_string = NewRawTwoByteString(character_count_);
1378 AssertNoAllocation no_alloc;
1379 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1380 uc16* char_buffer = seq->GetChars();
1381 StringBuilderConcatHelper(*subject_,
1382 char_buffer,
1383 *parts_,
1384 part_count_);
1385 }
1386 return joined_string;
1387 }
1388
1389
1390 void IncrementCharacterCount(int by) {
1391 if (character_count_ > Smi::kMaxValue - by) {
1392 V8::FatalProcessOutOfMemory("String.replace result too large.");
1393 }
1394 character_count_ += by;
1395 }
1396
1397 private:
1398
1399 Handle<String> NewRawAsciiString(int size) {
1400 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1401 }
1402
1403
1404 Handle<String> NewRawTwoByteString(int size) {
1405 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1406 }
1407
1408
1409 void AddElement(Object* element) {
1410 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001411 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001412 parts_->set(part_count_, element);
1413 part_count_++;
1414 }
1415
1416 Handle<String> subject_;
1417 Handle<FixedArray> parts_;
1418 int part_count_;
1419 int character_count_;
1420 bool is_ascii_;
1421};
1422
1423
1424class CompiledReplacement {
1425 public:
1426 CompiledReplacement()
1427 : parts_(1), replacement_substrings_(0) {}
1428
1429 void Compile(Handle<String> replacement,
1430 int capture_count,
1431 int subject_length);
1432
1433 void Apply(ReplacementStringBuilder* builder,
1434 int match_from,
1435 int match_to,
1436 Handle<JSArray> last_match_info);
1437
1438 // Number of distinct parts of the replacement pattern.
1439 int parts() {
1440 return parts_.length();
1441 }
1442 private:
1443 enum PartType {
1444 SUBJECT_PREFIX = 1,
1445 SUBJECT_SUFFIX,
1446 SUBJECT_CAPTURE,
1447 REPLACEMENT_SUBSTRING,
1448 REPLACEMENT_STRING,
1449
1450 NUMBER_OF_PART_TYPES
1451 };
1452
1453 struct ReplacementPart {
1454 static inline ReplacementPart SubjectMatch() {
1455 return ReplacementPart(SUBJECT_CAPTURE, 0);
1456 }
1457 static inline ReplacementPart SubjectCapture(int capture_index) {
1458 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1459 }
1460 static inline ReplacementPart SubjectPrefix() {
1461 return ReplacementPart(SUBJECT_PREFIX, 0);
1462 }
1463 static inline ReplacementPart SubjectSuffix(int subject_length) {
1464 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1465 }
1466 static inline ReplacementPart ReplacementString() {
1467 return ReplacementPart(REPLACEMENT_STRING, 0);
1468 }
1469 static inline ReplacementPart ReplacementSubString(int from, int to) {
1470 ASSERT(from >= 0);
1471 ASSERT(to > from);
1472 return ReplacementPart(-from, to);
1473 }
1474
1475 // If tag <= 0 then it is the negation of a start index of a substring of
1476 // the replacement pattern, otherwise it's a value from PartType.
1477 ReplacementPart(int tag, int data)
1478 : tag(tag), data(data) {
1479 // Must be non-positive or a PartType value.
1480 ASSERT(tag < NUMBER_OF_PART_TYPES);
1481 }
1482 // Either a value of PartType or a non-positive number that is
1483 // the negation of an index into the replacement string.
1484 int tag;
1485 // The data value's interpretation depends on the value of tag:
1486 // tag == SUBJECT_PREFIX ||
1487 // tag == SUBJECT_SUFFIX: data is unused.
1488 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1489 // tag == REPLACEMENT_SUBSTRING ||
1490 // tag == REPLACEMENT_STRING: data is index into array of substrings
1491 // of the replacement string.
1492 // tag <= 0: Temporary representation of the substring of the replacement
1493 // string ranging over -tag .. data.
1494 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1495 // substring objects.
1496 int data;
1497 };
1498
1499 template<typename Char>
1500 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1501 Vector<Char> characters,
1502 int capture_count,
1503 int subject_length) {
1504 int length = characters.length();
1505 int last = 0;
1506 for (int i = 0; i < length; i++) {
1507 Char c = characters[i];
1508 if (c == '$') {
1509 int next_index = i + 1;
1510 if (next_index == length) { // No next character!
1511 break;
1512 }
1513 Char c2 = characters[next_index];
1514 switch (c2) {
1515 case '$':
1516 if (i > last) {
1517 // There is a substring before. Include the first "$".
1518 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1519 last = next_index + 1; // Continue after the second "$".
1520 } else {
1521 // Let the next substring start with the second "$".
1522 last = next_index;
1523 }
1524 i = next_index;
1525 break;
1526 case '`':
1527 if (i > last) {
1528 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1529 }
1530 parts->Add(ReplacementPart::SubjectPrefix());
1531 i = next_index;
1532 last = i + 1;
1533 break;
1534 case '\'':
1535 if (i > last) {
1536 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1537 }
1538 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1539 i = next_index;
1540 last = i + 1;
1541 break;
1542 case '&':
1543 if (i > last) {
1544 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1545 }
1546 parts->Add(ReplacementPart::SubjectMatch());
1547 i = next_index;
1548 last = i + 1;
1549 break;
1550 case '0':
1551 case '1':
1552 case '2':
1553 case '3':
1554 case '4':
1555 case '5':
1556 case '6':
1557 case '7':
1558 case '8':
1559 case '9': {
1560 int capture_ref = c2 - '0';
1561 if (capture_ref > capture_count) {
1562 i = next_index;
1563 continue;
1564 }
1565 int second_digit_index = next_index + 1;
1566 if (second_digit_index < length) {
1567 // Peek ahead to see if we have two digits.
1568 Char c3 = characters[second_digit_index];
1569 if ('0' <= c3 && c3 <= '9') { // Double digits.
1570 int double_digit_ref = capture_ref * 10 + c3 - '0';
1571 if (double_digit_ref <= capture_count) {
1572 next_index = second_digit_index;
1573 capture_ref = double_digit_ref;
1574 }
1575 }
1576 }
1577 if (capture_ref > 0) {
1578 if (i > last) {
1579 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1580 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001581 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001582 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1583 last = next_index + 1;
1584 }
1585 i = next_index;
1586 break;
1587 }
1588 default:
1589 i = next_index;
1590 break;
1591 }
1592 }
1593 }
1594 if (length > last) {
1595 if (last == 0) {
1596 parts->Add(ReplacementPart::ReplacementString());
1597 } else {
1598 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1599 }
1600 }
1601 }
1602
1603 ZoneList<ReplacementPart> parts_;
1604 ZoneList<Handle<String> > replacement_substrings_;
1605};
1606
1607
1608void CompiledReplacement::Compile(Handle<String> replacement,
1609 int capture_count,
1610 int subject_length) {
1611 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001612 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001613 AssertNoAllocation no_alloc;
1614 ParseReplacementPattern(&parts_,
1615 replacement->ToAsciiVector(),
1616 capture_count,
1617 subject_length);
1618 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001619 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001620 AssertNoAllocation no_alloc;
1621
1622 ParseReplacementPattern(&parts_,
1623 replacement->ToUC16Vector(),
1624 capture_count,
1625 subject_length);
1626 }
1627 // Find substrings of replacement string and create them as String objects..
1628 int substring_index = 0;
1629 for (int i = 0, n = parts_.length(); i < n; i++) {
1630 int tag = parts_[i].tag;
1631 if (tag <= 0) { // A replacement string slice.
1632 int from = -tag;
1633 int to = parts_[i].data;
1634 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1635 from,
1636 to));
1637 parts_[i].tag = REPLACEMENT_SUBSTRING;
1638 parts_[i].data = substring_index;
1639 substring_index++;
1640 } else if (tag == REPLACEMENT_STRING) {
1641 replacement_substrings_.Add(replacement);
1642 parts_[i].data = substring_index;
1643 substring_index++;
1644 }
1645 }
1646}
1647
1648
1649void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1650 int match_from,
1651 int match_to,
1652 Handle<JSArray> last_match_info) {
1653 for (int i = 0, n = parts_.length(); i < n; i++) {
1654 ReplacementPart part = parts_[i];
1655 switch (part.tag) {
1656 case SUBJECT_PREFIX:
1657 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1658 break;
1659 case SUBJECT_SUFFIX: {
1660 int subject_length = part.data;
1661 if (match_to < subject_length) {
1662 builder->AddSubjectSlice(match_to, subject_length);
1663 }
1664 break;
1665 }
1666 case SUBJECT_CAPTURE: {
1667 int capture = part.data;
1668 FixedArray* match_info = last_match_info->elements();
1669 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1670 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1671 if (from >= 0 && to > from) {
1672 builder->AddSubjectSlice(from, to);
1673 }
1674 break;
1675 }
1676 case REPLACEMENT_SUBSTRING:
1677 case REPLACEMENT_STRING:
1678 builder->AddString(replacement_substrings_[part.data]);
1679 break;
1680 default:
1681 UNREACHABLE();
1682 }
1683 }
1684}
1685
1686
1687
1688static Object* StringReplaceRegExpWithString(String* subject,
1689 JSRegExp* regexp,
1690 String* replacement,
1691 JSArray* last_match_info) {
1692 ASSERT(subject->IsFlat());
1693 ASSERT(replacement->IsFlat());
1694
1695 HandleScope handles;
1696
1697 int length = subject->length();
1698 Handle<String> subject_handle(subject);
1699 Handle<JSRegExp> regexp_handle(regexp);
1700 Handle<String> replacement_handle(replacement);
1701 Handle<JSArray> last_match_info_handle(last_match_info);
1702 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1703 subject_handle,
1704 0,
1705 last_match_info_handle);
1706 if (match.is_null()) {
1707 return Failure::Exception();
1708 }
1709 if (match->IsNull()) {
1710 return *subject_handle;
1711 }
1712
1713 int capture_count = regexp_handle->CaptureCount();
1714
1715 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001716 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001717 CompiledReplacement compiled_replacement;
1718 compiled_replacement.Compile(replacement_handle,
1719 capture_count,
1720 length);
1721
1722 bool is_global = regexp_handle->GetFlags().is_global();
1723
1724 // Guessing the number of parts that the final result string is built
1725 // from. Global regexps can match any number of times, so we guess
1726 // conservatively.
1727 int expected_parts =
1728 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1729 ReplacementStringBuilder builder(subject_handle, expected_parts);
1730
1731 // Index of end of last match.
1732 int prev = 0;
1733
1734 // Number of parts added by compiled replacement plus preceeding string
1735 // and possibly suffix after last match.
1736 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1737 bool matched = true;
1738 do {
1739 ASSERT(last_match_info_handle->HasFastElements());
1740 // Increase the capacity of the builder before entering local handle-scope,
1741 // so its internal buffer can safely allocate a new handle if it grows.
1742 builder.EnsureCapacity(parts_added_per_loop);
1743
1744 HandleScope loop_scope;
1745 int start, end;
1746 {
1747 AssertNoAllocation match_info_array_is_not_in_a_handle;
1748 FixedArray* match_info_array = last_match_info_handle->elements();
1749
1750 ASSERT_EQ(capture_count * 2 + 2,
1751 RegExpImpl::GetLastCaptureCount(match_info_array));
1752 start = RegExpImpl::GetCapture(match_info_array, 0);
1753 end = RegExpImpl::GetCapture(match_info_array, 1);
1754 }
1755
1756 if (prev < start) {
1757 builder.AddSubjectSlice(prev, start);
1758 }
1759 compiled_replacement.Apply(&builder,
1760 start,
1761 end,
1762 last_match_info_handle);
1763 prev = end;
1764
1765 // Only continue checking for global regexps.
1766 if (!is_global) break;
1767
1768 // Continue from where the match ended, unless it was an empty match.
1769 int next = end;
1770 if (start == end) {
1771 next = end + 1;
1772 if (next > length) break;
1773 }
1774
1775 match = RegExpImpl::Exec(regexp_handle,
1776 subject_handle,
1777 next,
1778 last_match_info_handle);
1779 if (match.is_null()) {
1780 return Failure::Exception();
1781 }
1782 matched = !match->IsNull();
1783 } while (matched);
1784
1785 if (prev < length) {
1786 builder.AddSubjectSlice(prev, length);
1787 }
1788
1789 return *(builder.ToString());
1790}
1791
1792
1793static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1794 ASSERT(args.length() == 4);
1795
1796 CONVERT_CHECKED(String, subject, args[0]);
1797 if (!subject->IsFlat()) {
1798 Object* flat_subject = subject->TryFlatten();
1799 if (flat_subject->IsFailure()) {
1800 return flat_subject;
1801 }
1802 subject = String::cast(flat_subject);
1803 }
1804
1805 CONVERT_CHECKED(String, replacement, args[2]);
1806 if (!replacement->IsFlat()) {
1807 Object* flat_replacement = replacement->TryFlatten();
1808 if (flat_replacement->IsFailure()) {
1809 return flat_replacement;
1810 }
1811 replacement = String::cast(flat_replacement);
1812 }
1813
1814 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1815 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1816
1817 ASSERT(last_match_info->HasFastElements());
1818
1819 return StringReplaceRegExpWithString(subject,
1820 regexp,
1821 replacement,
1822 last_match_info);
1823}
1824
1825
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826
ager@chromium.org7c537e22008-10-16 08:43:32 +00001827// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1828// limit, we can fix the size of tables.
1829static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001830// Reduce alphabet to this size.
1831static const int kBMAlphabetSize = 0x100;
1832// For patterns below this length, the skip length of Boyer-Moore is too short
1833// to compensate for the algorithmic overhead compared to simple brute force.
1834static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001835
ager@chromium.org7c537e22008-10-16 08:43:32 +00001836// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1837// shift. Only allows the last kBMMaxShift characters of the needle
1838// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001839class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001840 public:
1841 BMGoodSuffixBuffers() {}
1842 inline void init(int needle_length) {
1843 ASSERT(needle_length > 1);
1844 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1845 int len = needle_length - start;
1846 biased_suffixes_ = suffixes_ - start;
1847 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1848 for (int i = 0; i <= len; i++) {
1849 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001850 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001851 }
1852 inline int& suffix(int index) {
1853 ASSERT(biased_suffixes_ + index >= suffixes_);
1854 return biased_suffixes_[index];
1855 }
1856 inline int& shift(int index) {
1857 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1858 return biased_good_suffix_shift_[index];
1859 }
1860 private:
1861 int suffixes_[kBMMaxShift + 1];
1862 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001863 int* biased_suffixes_;
1864 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001865 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1866};
1867
1868// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001869static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001870static BMGoodSuffixBuffers bmgs_buffers;
1871
1872// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001873template <typename pchar>
1874static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1875 int start) {
1876 // Run forwards to populate bad_char_table, so that *last* instance
1877 // of character equivalence class is the one registered.
1878 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001879 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1880 : kBMAlphabetSize;
1881 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001882 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001883 } else {
1884 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001885 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001886 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001887 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001888 for (int i = start; i < pattern.length() - 1; i++) {
1889 pchar c = pattern[i];
1890 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001891 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001892 }
1893}
1894
1895template <typename pchar>
1896static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001897 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001898 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001899 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001900 // Compute Good Suffix tables.
1901 bmgs_buffers.init(m);
1902
1903 bmgs_buffers.shift(m-1) = 1;
1904 bmgs_buffers.suffix(m) = m + 1;
1905 pchar last_char = pattern[m - 1];
1906 int suffix = m + 1;
1907 for (int i = m; i > start;) {
1908 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1909 if (bmgs_buffers.shift(suffix) == len) {
1910 bmgs_buffers.shift(suffix) = suffix - i;
1911 }
1912 suffix = bmgs_buffers.suffix(suffix);
1913 }
1914 i--;
1915 suffix--;
1916 bmgs_buffers.suffix(i) = suffix;
1917 if (suffix == m) {
1918 // No suffix to extend, so we check against last_char only.
1919 while (i > start && pattern[i - 1] != last_char) {
1920 if (bmgs_buffers.shift(m) == len) {
1921 bmgs_buffers.shift(m) = m - i;
1922 }
1923 i--;
1924 bmgs_buffers.suffix(i) = m;
1925 }
1926 if (i > start) {
1927 i--;
1928 suffix--;
1929 bmgs_buffers.suffix(i) = suffix;
1930 }
1931 }
1932 }
1933 if (suffix < m) {
1934 for (int i = start; i <= m; i++) {
1935 if (bmgs_buffers.shift(i) == len) {
1936 bmgs_buffers.shift(i) = suffix - start;
1937 }
1938 if (i == suffix) {
1939 suffix = bmgs_buffers.suffix(suffix);
1940 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001941 }
1942 }
1943}
1944
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001945template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001946static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001947 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001948 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001949 }
1950 if (sizeof(pchar) == 1) {
1951 if (char_code > String::kMaxAsciiCharCode) {
1952 return -1;
1953 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001954 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001955 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001956 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001957}
1958
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001959// Restricted simplified Boyer-Moore string matching.
1960// Uses only the bad-shift table of Boyer-Moore and only uses it
1961// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001962template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001963static int BoyerMooreHorspool(Vector<const schar> subject,
1964 Vector<const pchar> pattern,
1965 int start_index,
1966 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001967 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001968 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001969 // Only preprocess at most kBMMaxShift last characters of pattern.
1970 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001971
ager@chromium.org7c537e22008-10-16 08:43:32 +00001972 BoyerMoorePopulateBadCharTable(pattern, start);
1973
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001974 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001975 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001976 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001977 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001978 // Perform search
1979 for (idx = start_index; idx <= n - m;) {
1980 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001981 int c;
1982 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001983 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001984 int shift = j - bc_occ;
1985 idx += shift;
1986 badness += 1 - shift; // at most zero, so badness cannot increase.
1987 if (idx > n - m) {
1988 *complete = true;
1989 return -1;
1990 }
1991 }
1992 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001993 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001994 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001995 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001996 return idx;
1997 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001998 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001999 // Badness increases by the number of characters we have
2000 // checked, and decreases by the number of characters we
2001 // can skip by shifting. It's a measure of how we are doing
2002 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002003 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002004 if (badness > 0) {
2005 *complete = false;
2006 return idx;
2007 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002008 }
2009 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002010 *complete = true;
2011 return -1;
2012}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002013
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002014
2015template <typename schar, typename pchar>
2016static int BoyerMooreIndexOf(Vector<const schar> subject,
2017 Vector<const pchar> pattern,
2018 int idx) {
2019 int n = subject.length();
2020 int m = pattern.length();
2021 // Only preprocess at most kBMMaxShift last characters of pattern.
2022 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2023
2024 // Build the Good Suffix table and continue searching.
2025 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2026 pchar last_char = pattern[m - 1];
2027 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002028 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002029 int j = m - 1;
2030 schar c;
2031 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002032 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002033 idx += shift;
2034 if (idx > n - m) {
2035 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002036 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002037 }
2038 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2039 if (j < 0) {
2040 return idx;
2041 } else if (j < start) {
2042 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002043 // Fall back on BMH shift.
2044 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002045 } else {
2046 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002047 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002048 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002049 if (gs_shift > shift) {
2050 shift = gs_shift;
2051 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002052 idx += shift;
2053 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002054 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002055
2056 return -1;
2057}
2058
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002059
2060template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002061static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002062 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002063 int start_index) {
2064 for (int i = start_index, n = string.length(); i < n; i++) {
2065 if (pattern_char == string[i]) {
2066 return i;
2067 }
2068 }
2069 return -1;
2070}
2071
2072// Trivial string search for shorter strings.
2073// On return, if "complete" is set to true, the return value is the
2074// final result of searching for the patter in the subject.
2075// If "complete" is set to false, the return value is the index where
2076// further checking should start, i.e., it's guaranteed that the pattern
2077// does not occur at a position prior to the returned index.
2078template <typename pchar, typename schar>
2079static int SimpleIndexOf(Vector<const schar> subject,
2080 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002081 int idx,
2082 bool* complete) {
2083 // Badness is a count of how much work we have done. When we have
2084 // done enough work we decide it's probably worth switching to a better
2085 // algorithm.
2086 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002087 // We know our pattern is at least 2 characters, we cache the first so
2088 // the common case of the first character not matching is faster.
2089 pchar pattern_first_char = pattern[0];
2090
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002091 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2092 badness++;
2093 if (badness > 0) {
2094 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002095 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002096 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002097 if (subject[i] != pattern_first_char) continue;
2098 int j = 1;
2099 do {
2100 if (pattern[j] != subject[i+j]) {
2101 break;
2102 }
2103 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002104 } while (j < pattern.length());
2105 if (j == pattern.length()) {
2106 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002107 return i;
2108 }
2109 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002110 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002111 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002112 return -1;
2113}
2114
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002115// Simple indexOf that never bails out. For short patterns only.
2116template <typename pchar, typename schar>
2117static int SimpleIndexOf(Vector<const schar> subject,
2118 Vector<const pchar> pattern,
2119 int idx) {
2120 pchar pattern_first_char = pattern[0];
2121 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2122 if (subject[i] != pattern_first_char) continue;
2123 int j = 1;
2124 do {
2125 if (pattern[j] != subject[i+j]) {
2126 break;
2127 }
2128 j++;
2129 } while (j < pattern.length());
2130 if (j == pattern.length()) {
2131 return i;
2132 }
2133 }
2134 return -1;
2135}
2136
2137
2138// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002139template <typename schar, typename pchar>
2140static int StringMatchStrategy(Vector<const schar> sub,
2141 Vector<const pchar> pat,
2142 int start_index) {
2143 ASSERT(pat.length() > 1);
2144
2145 // We have an ASCII haystack and a non-ASCII needle. Check if there
2146 // really is a non-ASCII character in the needle and bail out if there
2147 // is.
2148 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2149 for (int i = 0; i < pat.length(); i++) {
2150 uc16 c = pat[i];
2151 if (c > String::kMaxAsciiCharCode) {
2152 return -1;
2153 }
2154 }
2155 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002156 if (pat.length() < kBMMinPatternLength) {
2157 // We don't believe fancy searching can ever be more efficient.
2158 // The max shift of Boyer-Moore on a pattern of this length does
2159 // not compensate for the overhead.
2160 return SimpleIndexOf(sub, pat, start_index);
2161 }
2162 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002163 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002164 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2165 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002166 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002167 if (complete) return idx;
2168 return BoyerMooreIndexOf(sub, pat, idx);
2169}
2170
2171// Perform string match of pattern on subject, starting at start index.
2172// Caller must ensure that 0 <= start_index <= sub->length(),
2173// and should check that pat->length() + start_index <= sub->length()
2174int Runtime::StringMatch(Handle<String> sub,
2175 Handle<String> pat,
2176 int start_index) {
2177 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002178 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002179
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002180 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002181 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002182
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002183 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002184 if (start_index + pattern_length > subject_length) return -1;
2185
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002186 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002187 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002188 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002189 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002190 // character patterns linear search is necessary, so any smart
2191 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002192 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002193 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002194 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002195 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002196 if (pchar > String::kMaxAsciiCharCode) {
2197 return -1;
2198 }
2199 Vector<const char> ascii_vector =
2200 sub->ToAsciiVector().SubVector(start_index, subject_length);
2201 const void* pos = memchr(ascii_vector.start(),
2202 static_cast<const char>(pchar),
2203 static_cast<size_t>(ascii_vector.length()));
2204 if (pos == NULL) {
2205 return -1;
2206 }
2207 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2208 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002209 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002210 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002211 }
2212
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002213 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002214 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002215 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002216
ager@chromium.org7c537e22008-10-16 08:43:32 +00002217 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2218 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002219 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002220 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002221 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002222 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002223 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002224 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002225 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002226 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002227 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002228 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002229 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002230 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002231}
2232
2233
2234static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002235 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002236 ASSERT(args.length() == 3);
2237
ager@chromium.org7c537e22008-10-16 08:43:32 +00002238 CONVERT_ARG_CHECKED(String, sub, 0);
2239 CONVERT_ARG_CHECKED(String, pat, 1);
2240
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002241 Object* index = args[2];
2242 uint32_t start_index;
2243 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2244
ager@chromium.org870a0b62008-11-04 11:43:05 +00002245 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002246 int position = Runtime::StringMatch(sub, pat, start_index);
2247 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002248}
2249
2250
2251static Object* Runtime_StringLastIndexOf(Arguments args) {
2252 NoHandleAllocation ha;
2253 ASSERT(args.length() == 3);
2254
2255 CONVERT_CHECKED(String, sub, args[0]);
2256 CONVERT_CHECKED(String, pat, args[1]);
2257 Object* index = args[2];
2258
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002259 sub->TryFlattenIfNotFlat();
2260 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002261
2262 uint32_t start_index;
2263 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2264
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002265 uint32_t pattern_length = pat->length();
2266 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002267
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002268 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002269 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002270 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002271
2272 for (int i = start_index; i >= 0; i--) {
2273 bool found = true;
2274 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002275 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002276 found = false;
2277 break;
2278 }
2279 }
2280 if (found) return Smi::FromInt(i);
2281 }
2282
2283 return Smi::FromInt(-1);
2284}
2285
2286
2287static Object* Runtime_StringLocaleCompare(Arguments args) {
2288 NoHandleAllocation ha;
2289 ASSERT(args.length() == 2);
2290
2291 CONVERT_CHECKED(String, str1, args[0]);
2292 CONVERT_CHECKED(String, str2, args[1]);
2293
2294 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002295 int str1_length = str1->length();
2296 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002297
2298 // Decide trivial cases without flattening.
2299 if (str1_length == 0) {
2300 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2301 return Smi::FromInt(-str2_length);
2302 } else {
2303 if (str2_length == 0) return Smi::FromInt(str1_length);
2304 }
2305
2306 int end = str1_length < str2_length ? str1_length : str2_length;
2307
2308 // No need to flatten if we are going to find the answer on the first
2309 // character. At this point we know there is at least one character
2310 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002311 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002312 if (d != 0) return Smi::FromInt(d);
2313
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002314 str1->TryFlattenIfNotFlat();
2315 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002316
2317 static StringInputBuffer buf1;
2318 static StringInputBuffer buf2;
2319
2320 buf1.Reset(str1);
2321 buf2.Reset(str2);
2322
2323 for (int i = 0; i < end; i++) {
2324 uint16_t char1 = buf1.GetNext();
2325 uint16_t char2 = buf2.GetNext();
2326 if (char1 != char2) return Smi::FromInt(char1 - char2);
2327 }
2328
2329 return Smi::FromInt(str1_length - str2_length);
2330}
2331
2332
2333static Object* Runtime_StringSlice(Arguments args) {
2334 NoHandleAllocation ha;
2335 ASSERT(args.length() == 3);
2336
2337 CONVERT_CHECKED(String, value, args[0]);
2338 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2339 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2340
2341 int start = FastD2I(from_number);
2342 int end = FastD2I(to_number);
2343
2344 RUNTIME_ASSERT(end >= start);
2345 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002346 RUNTIME_ASSERT(end <= value->length());
2347 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002348}
2349
2350
ager@chromium.org41826e72009-03-30 13:30:57 +00002351static Object* Runtime_StringMatch(Arguments args) {
2352 ASSERT_EQ(3, args.length());
2353
2354 CONVERT_ARG_CHECKED(String, subject, 0);
2355 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2356 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2357 HandleScope handles;
2358
2359 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2360
2361 if (match.is_null()) {
2362 return Failure::Exception();
2363 }
2364 if (match->IsNull()) {
2365 return Heap::null_value();
2366 }
2367 int length = subject->length();
2368
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002369 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002370 ZoneList<int> offsets(8);
2371 do {
2372 int start;
2373 int end;
2374 {
2375 AssertNoAllocation no_alloc;
2376 FixedArray* elements = regexp_info->elements();
2377 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2378 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2379 }
2380 offsets.Add(start);
2381 offsets.Add(end);
2382 int index = start < end ? end : end + 1;
2383 if (index > length) break;
2384 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2385 if (match.is_null()) {
2386 return Failure::Exception();
2387 }
2388 } while (!match->IsNull());
2389 int matches = offsets.length() / 2;
2390 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2391 for (int i = 0; i < matches ; i++) {
2392 int from = offsets.at(i * 2);
2393 int to = offsets.at(i * 2 + 1);
2394 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2395 }
2396 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2397 result->set_length(Smi::FromInt(matches));
2398 return *result;
2399}
2400
2401
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002402static Object* Runtime_NumberToRadixString(Arguments args) {
2403 NoHandleAllocation ha;
2404 ASSERT(args.length() == 2);
2405
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002406 // Fast case where the result is a one character string.
2407 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2408 int value = Smi::cast(args[0])->value();
2409 int radix = Smi::cast(args[1])->value();
2410 if (value >= 0 && value < radix) {
2411 RUNTIME_ASSERT(radix <= 36);
2412 // Character array used for conversion.
2413 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2414 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2415 }
2416 }
2417
2418 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002419 CONVERT_DOUBLE_CHECKED(value, args[0]);
2420 if (isnan(value)) {
2421 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2422 }
2423 if (isinf(value)) {
2424 if (value < 0) {
2425 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2426 }
2427 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2428 }
2429 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2430 int radix = FastD2I(radix_number);
2431 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2432 char* str = DoubleToRadixCString(value, radix);
2433 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2434 DeleteArray(str);
2435 return result;
2436}
2437
2438
2439static Object* Runtime_NumberToFixed(Arguments args) {
2440 NoHandleAllocation ha;
2441 ASSERT(args.length() == 2);
2442
2443 CONVERT_DOUBLE_CHECKED(value, args[0]);
2444 if (isnan(value)) {
2445 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2446 }
2447 if (isinf(value)) {
2448 if (value < 0) {
2449 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2450 }
2451 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2452 }
2453 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2454 int f = FastD2I(f_number);
2455 RUNTIME_ASSERT(f >= 0);
2456 char* str = DoubleToFixedCString(value, f);
2457 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2458 DeleteArray(str);
2459 return res;
2460}
2461
2462
2463static Object* Runtime_NumberToExponential(Arguments args) {
2464 NoHandleAllocation ha;
2465 ASSERT(args.length() == 2);
2466
2467 CONVERT_DOUBLE_CHECKED(value, args[0]);
2468 if (isnan(value)) {
2469 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2470 }
2471 if (isinf(value)) {
2472 if (value < 0) {
2473 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2474 }
2475 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2476 }
2477 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2478 int f = FastD2I(f_number);
2479 RUNTIME_ASSERT(f >= -1 && f <= 20);
2480 char* str = DoubleToExponentialCString(value, f);
2481 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2482 DeleteArray(str);
2483 return res;
2484}
2485
2486
2487static Object* Runtime_NumberToPrecision(Arguments args) {
2488 NoHandleAllocation ha;
2489 ASSERT(args.length() == 2);
2490
2491 CONVERT_DOUBLE_CHECKED(value, args[0]);
2492 if (isnan(value)) {
2493 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2494 }
2495 if (isinf(value)) {
2496 if (value < 0) {
2497 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2498 }
2499 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2500 }
2501 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2502 int f = FastD2I(f_number);
2503 RUNTIME_ASSERT(f >= 1 && f <= 21);
2504 char* str = DoubleToPrecisionCString(value, f);
2505 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2506 DeleteArray(str);
2507 return res;
2508}
2509
2510
2511// Returns a single character string where first character equals
2512// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002513static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002514 if (index < static_cast<uint32_t>(string->length())) {
2515 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002516 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002517 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002518 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002519 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002520}
2521
2522
2523Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2524 // Handle [] indexing on Strings
2525 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002526 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2527 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002528 }
2529
2530 // Handle [] indexing on String objects
2531 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002532 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2533 Handle<Object> result =
2534 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2535 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002536 }
2537
2538 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002539 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002540 return prototype->GetElement(index);
2541 }
2542
2543 return object->GetElement(index);
2544}
2545
2546
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002547Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2548 HandleScope scope;
2549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002550 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002551 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002552 Handle<Object> error =
2553 Factory::NewTypeError("non_object_property_load",
2554 HandleVector(args, 2));
2555 return Top::Throw(*error);
2556 }
2557
2558 // Check if the given key is an array index.
2559 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002560 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561 return GetElementOrCharAt(object, index);
2562 }
2563
2564 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002565 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002566 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002567 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002568 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569 bool has_pending_exception = false;
2570 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002571 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002572 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002573 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002574 }
2575
ager@chromium.org32912102009-01-16 10:38:43 +00002576 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002577 // the element if so.
2578 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002579 return GetElementOrCharAt(object, index);
2580 } else {
2581 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002582 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002583 }
2584}
2585
2586
2587static Object* Runtime_GetProperty(Arguments args) {
2588 NoHandleAllocation ha;
2589 ASSERT(args.length() == 2);
2590
2591 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002592 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002593
2594 return Runtime::GetObjectProperty(object, key);
2595}
2596
2597
ager@chromium.org7c537e22008-10-16 08:43:32 +00002598
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002599// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002600static Object* Runtime_KeyedGetProperty(Arguments args) {
2601 NoHandleAllocation ha;
2602 ASSERT(args.length() == 2);
2603
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002604 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002605 // itself.
2606 //
2607 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002608 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002609 // global proxy object never has properties. This is the case
2610 // because the global proxy object forwards everything to its hidden
2611 // prototype including local lookups.
2612 //
2613 // Additionally, we need to make sure that we do not cache results
2614 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 if (args[0]->IsJSObject() &&
2616 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002617 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002618 args[1]->IsString()) {
2619 JSObject* receiver = JSObject::cast(args[0]);
2620 String* key = String::cast(args[1]);
2621 if (receiver->HasFastProperties()) {
2622 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002624 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2625 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002626 Object* value = receiver->FastPropertyAt(offset);
2627 return value->IsTheHole() ? Heap::undefined_value() : value;
2628 }
2629 // Lookup cache miss. Perform lookup and update the cache if
2630 // appropriate.
2631 LookupResult result;
2632 receiver->LocalLookup(key, &result);
2633 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2634 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002635 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002636 Object* value = receiver->FastPropertyAt(offset);
2637 return value->IsTheHole() ? Heap::undefined_value() : value;
2638 }
2639 } else {
2640 // Attempt dictionary lookup.
2641 Dictionary* dictionary = receiver->property_dictionary();
2642 int entry = dictionary->FindStringEntry(key);
2643 if ((entry != DescriptorArray::kNotFound) &&
2644 (dictionary->DetailsAt(entry).type() == NORMAL)) {
2645 return dictionary->ValueAt(entry);
2646 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002647 }
2648 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002649
2650 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002651 return Runtime::GetObjectProperty(args.at<Object>(0),
2652 args.at<Object>(1));
2653}
2654
2655
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002656Object* Runtime::SetObjectProperty(Handle<Object> object,
2657 Handle<Object> key,
2658 Handle<Object> value,
2659 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002660 HandleScope scope;
2661
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002662 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002663 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002664 Handle<Object> error =
2665 Factory::NewTypeError("non_object_property_store",
2666 HandleVector(args, 2));
2667 return Top::Throw(*error);
2668 }
2669
2670 // If the object isn't a JavaScript object, we ignore the store.
2671 if (!object->IsJSObject()) return *value;
2672
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002673 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2674
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002675 // Check if the given key is an array index.
2676 uint32_t index;
2677 if (Array::IndexFromObject(*key, &index)) {
2678 ASSERT(attr == NONE);
2679
2680 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2681 // of a string using [] notation. We need to support this too in
2682 // JavaScript.
2683 // In the case of a String object we just need to redirect the assignment to
2684 // the underlying string if the index is in range. Since the underlying
2685 // string does nothing with the assignment then we can ignore such
2686 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002687 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002688 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002689 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002690
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002691 Handle<Object> result = SetElement(js_object, index, value);
2692 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002693 return *value;
2694 }
2695
2696 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002697 Handle<Object> result;
2698 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002699 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002700 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002701 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002702 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002703 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002704 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002705 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002706 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002707 return *value;
2708 }
2709
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002710 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002711 bool has_pending_exception = false;
2712 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2713 if (has_pending_exception) return Failure::Exception();
2714 Handle<String> name = Handle<String>::cast(converted);
2715
2716 if (name->AsArrayIndex(&index)) {
2717 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002718 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002719 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002720 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002721 }
2722}
2723
2724
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002725Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2726 Handle<Object> key,
2727 Handle<Object> value,
2728 PropertyAttributes attr) {
2729 HandleScope scope;
2730
2731 // Check if the given key is an array index.
2732 uint32_t index;
2733 if (Array::IndexFromObject(*key, &index)) {
2734 ASSERT(attr == NONE);
2735
2736 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2737 // of a string using [] notation. We need to support this too in
2738 // JavaScript.
2739 // In the case of a String object we just need to redirect the assignment to
2740 // the underlying string if the index is in range. Since the underlying
2741 // string does nothing with the assignment then we can ignore such
2742 // assignments.
2743 if (js_object->IsStringObjectWithCharacterAt(index)) {
2744 return *value;
2745 }
2746
2747 return js_object->SetElement(index, *value);
2748 }
2749
2750 if (key->IsString()) {
2751 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2752 ASSERT(attr == NONE);
2753 return js_object->SetElement(index, *value);
2754 } else {
2755 Handle<String> key_string = Handle<String>::cast(key);
2756 key_string->TryFlattenIfNotFlat();
2757 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2758 *value,
2759 attr);
2760 }
2761 }
2762
2763 // Call-back into JavaScript to convert the key to a string.
2764 bool has_pending_exception = false;
2765 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2766 if (has_pending_exception) return Failure::Exception();
2767 Handle<String> name = Handle<String>::cast(converted);
2768
2769 if (name->AsArrayIndex(&index)) {
2770 ASSERT(attr == NONE);
2771 return js_object->SetElement(index, *value);
2772 } else {
2773 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2774 }
2775}
2776
2777
ager@chromium.orge2902be2009-06-08 12:21:35 +00002778Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2779 Handle<Object> key) {
2780 HandleScope scope;
2781
2782 // Check if the given key is an array index.
2783 uint32_t index;
2784 if (Array::IndexFromObject(*key, &index)) {
2785 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2786 // characters of a string using [] notation. In the case of a
2787 // String object we just need to redirect the deletion to the
2788 // underlying string if the index is in range. Since the
2789 // underlying string does nothing with the deletion, we can ignore
2790 // such deletions.
2791 if (js_object->IsStringObjectWithCharacterAt(index)) {
2792 return Heap::true_value();
2793 }
2794
2795 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2796 }
2797
2798 Handle<String> key_string;
2799 if (key->IsString()) {
2800 key_string = Handle<String>::cast(key);
2801 } else {
2802 // Call-back into JavaScript to convert the key to a string.
2803 bool has_pending_exception = false;
2804 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2805 if (has_pending_exception) return Failure::Exception();
2806 key_string = Handle<String>::cast(converted);
2807 }
2808
2809 key_string->TryFlattenIfNotFlat();
2810 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2811}
2812
2813
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002814static Object* Runtime_SetProperty(Arguments args) {
2815 NoHandleAllocation ha;
2816 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2817
2818 Handle<Object> object = args.at<Object>(0);
2819 Handle<Object> key = args.at<Object>(1);
2820 Handle<Object> value = args.at<Object>(2);
2821
2822 // Compute attributes.
2823 PropertyAttributes attributes = NONE;
2824 if (args.length() == 4) {
2825 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002826 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002827 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002828 RUNTIME_ASSERT(
2829 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2830 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002831 }
2832 return Runtime::SetObjectProperty(object, key, value, attributes);
2833}
2834
2835
2836// Set a local property, even if it is READ_ONLY. If the property does not
2837// exist, it will be added with attributes NONE.
2838static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2839 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002840 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841 CONVERT_CHECKED(JSObject, object, args[0]);
2842 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002843 // Compute attributes.
2844 PropertyAttributes attributes = NONE;
2845 if (args.length() == 4) {
2846 CONVERT_CHECKED(Smi, value_obj, args[3]);
2847 int unchecked_value = value_obj->value();
2848 // Only attribute bits should be set.
2849 RUNTIME_ASSERT(
2850 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2851 attributes = static_cast<PropertyAttributes>(unchecked_value);
2852 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002853
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002854 return object->
2855 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002856}
2857
2858
2859static Object* Runtime_DeleteProperty(Arguments args) {
2860 NoHandleAllocation ha;
2861 ASSERT(args.length() == 2);
2862
2863 CONVERT_CHECKED(JSObject, object, args[0]);
2864 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002865 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002866}
2867
2868
ager@chromium.org9085a012009-05-11 19:22:57 +00002869static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2870 Handle<String> key) {
2871 if (object->HasLocalProperty(*key)) return Heap::true_value();
2872 // Handle hidden prototypes. If there's a hidden prototype above this thing
2873 // then we have to check it for properties, because they are supposed to
2874 // look like they are on this object.
2875 Handle<Object> proto(object->GetPrototype());
2876 if (proto->IsJSObject() &&
2877 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2878 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2879 }
2880 return Heap::false_value();
2881}
2882
2883
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002884static Object* Runtime_HasLocalProperty(Arguments args) {
2885 NoHandleAllocation ha;
2886 ASSERT(args.length() == 2);
2887 CONVERT_CHECKED(String, key, args[1]);
2888
ager@chromium.org9085a012009-05-11 19:22:57 +00002889 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002890 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002891 if (obj->IsJSObject()) {
2892 JSObject* object = JSObject::cast(obj);
2893 // Fast case - no interceptors.
2894 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2895 // Slow case. Either it's not there or we have an interceptor. We should
2896 // have handles for this kind of deal.
2897 HandleScope scope;
2898 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2899 Handle<String>(key));
2900 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002901 // Well, there is one exception: Handle [] on strings.
2902 uint32_t index;
2903 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002904 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002905 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002906 return Heap::true_value();
2907 }
2908 }
2909 return Heap::false_value();
2910}
2911
2912
2913static Object* Runtime_HasProperty(Arguments args) {
2914 NoHandleAllocation na;
2915 ASSERT(args.length() == 2);
2916
2917 // Only JS objects can have properties.
2918 if (args[0]->IsJSObject()) {
2919 JSObject* object = JSObject::cast(args[0]);
2920 CONVERT_CHECKED(String, key, args[1]);
2921 if (object->HasProperty(key)) return Heap::true_value();
2922 }
2923 return Heap::false_value();
2924}
2925
2926
2927static Object* Runtime_HasElement(Arguments args) {
2928 NoHandleAllocation na;
2929 ASSERT(args.length() == 2);
2930
2931 // Only JS objects can have elements.
2932 if (args[0]->IsJSObject()) {
2933 JSObject* object = JSObject::cast(args[0]);
2934 CONVERT_CHECKED(Smi, index_obj, args[1]);
2935 uint32_t index = index_obj->value();
2936 if (object->HasElement(index)) return Heap::true_value();
2937 }
2938 return Heap::false_value();
2939}
2940
2941
2942static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2943 NoHandleAllocation ha;
2944 ASSERT(args.length() == 2);
2945
2946 CONVERT_CHECKED(JSObject, object, args[0]);
2947 CONVERT_CHECKED(String, key, args[1]);
2948
2949 uint32_t index;
2950 if (key->AsArrayIndex(&index)) {
2951 return Heap::ToBoolean(object->HasElement(index));
2952 }
2953
ager@chromium.org870a0b62008-11-04 11:43:05 +00002954 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2955 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002956}
2957
2958
2959static Object* Runtime_GetPropertyNames(Arguments args) {
2960 HandleScope scope;
2961 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002962 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002963 return *GetKeysFor(object);
2964}
2965
2966
2967// Returns either a FixedArray as Runtime_GetPropertyNames,
2968// or, if the given object has an enum cache that contains
2969// all enumerable properties of the object and its prototypes
2970// have none, the map of the object. This is used to speed up
2971// the check for deletions during a for-in.
2972static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2973 ASSERT(args.length() == 1);
2974
2975 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2976
2977 if (raw_object->IsSimpleEnum()) return raw_object->map();
2978
2979 HandleScope scope;
2980 Handle<JSObject> object(raw_object);
2981 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2982
2983 // Test again, since cache may have been built by preceding call.
2984 if (object->IsSimpleEnum()) return object->map();
2985
2986 return *content;
2987}
2988
2989
2990static Object* Runtime_GetArgumentsProperty(Arguments args) {
2991 NoHandleAllocation ha;
2992 ASSERT(args.length() == 1);
2993
2994 // Compute the frame holding the arguments.
2995 JavaScriptFrameIterator it;
2996 it.AdvanceToArgumentsFrame();
2997 JavaScriptFrame* frame = it.frame();
2998
2999 // Get the actual number of provided arguments.
3000 const uint32_t n = frame->GetProvidedParametersCount();
3001
3002 // Try to convert the key to an index. If successful and within
3003 // index return the the argument from the frame.
3004 uint32_t index;
3005 if (Array::IndexFromObject(args[0], &index) && index < n) {
3006 return frame->GetParameter(index);
3007 }
3008
3009 // Convert the key to a string.
3010 HandleScope scope;
3011 bool exception = false;
3012 Handle<Object> converted =
3013 Execution::ToString(args.at<Object>(0), &exception);
3014 if (exception) return Failure::Exception();
3015 Handle<String> key = Handle<String>::cast(converted);
3016
3017 // Try to convert the string key into an array index.
3018 if (key->AsArrayIndex(&index)) {
3019 if (index < n) {
3020 return frame->GetParameter(index);
3021 } else {
3022 return Top::initial_object_prototype()->GetElement(index);
3023 }
3024 }
3025
3026 // Handle special arguments properties.
3027 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3028 if (key->Equals(Heap::callee_symbol())) return frame->function();
3029
3030 // Lookup in the initial Object.prototype object.
3031 return Top::initial_object_prototype()->GetProperty(*key);
3032}
3033
3034
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003035static Object* Runtime_ToFastProperties(Arguments args) {
3036 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003037 Handle<Object> object = args.at<Object>(0);
3038 if (object->IsJSObject()) {
3039 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3040 js_object->TransformToFastProperties(0);
3041 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003042 return *object;
3043}
3044
3045
3046static Object* Runtime_ToSlowProperties(Arguments args) {
3047 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003048 Handle<Object> object = args.at<Object>(0);
3049 if (object->IsJSObject()) {
3050 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3051 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
3052 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003053 return *object;
3054}
3055
3056
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003057static Object* Runtime_ToBool(Arguments args) {
3058 NoHandleAllocation ha;
3059 ASSERT(args.length() == 1);
3060
3061 return args[0]->ToBoolean();
3062}
3063
3064
3065// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3066// Possible optimizations: put the type string into the oddballs.
3067static Object* Runtime_Typeof(Arguments args) {
3068 NoHandleAllocation ha;
3069
3070 Object* obj = args[0];
3071 if (obj->IsNumber()) return Heap::number_symbol();
3072 HeapObject* heap_obj = HeapObject::cast(obj);
3073
3074 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003075 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003076
3077 InstanceType instance_type = heap_obj->map()->instance_type();
3078 if (instance_type < FIRST_NONSTRING_TYPE) {
3079 return Heap::string_symbol();
3080 }
3081
3082 switch (instance_type) {
3083 case ODDBALL_TYPE:
3084 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3085 return Heap::boolean_symbol();
3086 }
3087 if (heap_obj->IsNull()) {
3088 return Heap::object_symbol();
3089 }
3090 ASSERT(heap_obj->IsUndefined());
3091 return Heap::undefined_symbol();
3092 case JS_FUNCTION_TYPE:
3093 return Heap::function_symbol();
3094 default:
3095 // For any kind of object not handled above, the spec rule for
3096 // host objects gives that it is okay to return "object"
3097 return Heap::object_symbol();
3098 }
3099}
3100
3101
3102static Object* Runtime_StringToNumber(Arguments args) {
3103 NoHandleAllocation ha;
3104 ASSERT(args.length() == 1);
3105 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003106 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003107 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3108}
3109
3110
3111static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3112 NoHandleAllocation ha;
3113 ASSERT(args.length() == 1);
3114
3115 CONVERT_CHECKED(JSArray, codes, args[0]);
3116 int length = Smi::cast(codes->length())->value();
3117
3118 // Check if the string can be ASCII.
3119 int i;
3120 for (i = 0; i < length; i++) {
3121 Object* element = codes->GetElement(i);
3122 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3123 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3124 break;
3125 }
3126
3127 Object* object = NULL;
3128 if (i == length) { // The string is ASCII.
3129 object = Heap::AllocateRawAsciiString(length);
3130 } else { // The string is not ASCII.
3131 object = Heap::AllocateRawTwoByteString(length);
3132 }
3133
3134 if (object->IsFailure()) return object;
3135 String* result = String::cast(object);
3136 for (int i = 0; i < length; i++) {
3137 Object* element = codes->GetElement(i);
3138 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003139 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003140 }
3141 return result;
3142}
3143
3144
3145// kNotEscaped is generated by the following:
3146//
3147// #!/bin/perl
3148// for (my $i = 0; $i < 256; $i++) {
3149// print "\n" if $i % 16 == 0;
3150// my $c = chr($i);
3151// my $escaped = 1;
3152// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3153// print $escaped ? "0, " : "1, ";
3154// }
3155
3156
3157static bool IsNotEscaped(uint16_t character) {
3158 // Only for 8 bit characters, the rest are always escaped (in a different way)
3159 ASSERT(character < 256);
3160 static const char kNotEscaped[256] = {
3161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3164 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3165 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3166 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3167 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3168 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3177 };
3178 return kNotEscaped[character] != 0;
3179}
3180
3181
3182static Object* Runtime_URIEscape(Arguments args) {
3183 const char hex_chars[] = "0123456789ABCDEF";
3184 NoHandleAllocation ha;
3185 ASSERT(args.length() == 1);
3186 CONVERT_CHECKED(String, source, args[0]);
3187
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003188 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003189
3190 int escaped_length = 0;
3191 int length = source->length();
3192 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003193 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003194 buffer->Reset(source);
3195 while (buffer->has_more()) {
3196 uint16_t character = buffer->GetNext();
3197 if (character >= 256) {
3198 escaped_length += 6;
3199 } else if (IsNotEscaped(character)) {
3200 escaped_length++;
3201 } else {
3202 escaped_length += 3;
3203 }
3204 // We don't allow strings that are longer than Smi range.
3205 if (!Smi::IsValid(escaped_length)) {
3206 Top::context()->mark_out_of_memory();
3207 return Failure::OutOfMemoryException();
3208 }
3209 }
3210 }
3211 // No length change implies no change. Return original string if no change.
3212 if (escaped_length == length) {
3213 return source;
3214 }
3215 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3216 if (o->IsFailure()) return o;
3217 String* destination = String::cast(o);
3218 int dest_position = 0;
3219
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003220 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003221 buffer->Rewind();
3222 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003223 uint16_t chr = buffer->GetNext();
3224 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003225 destination->Set(dest_position, '%');
3226 destination->Set(dest_position+1, 'u');
3227 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3228 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3229 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3230 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003231 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003232 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003233 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003234 dest_position++;
3235 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003236 destination->Set(dest_position, '%');
3237 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3238 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003239 dest_position += 3;
3240 }
3241 }
3242 return destination;
3243}
3244
3245
3246static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3247 static const signed char kHexValue['g'] = {
3248 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3249 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3250 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3251 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3252 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3253 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3254 -1, 10, 11, 12, 13, 14, 15 };
3255
3256 if (character1 > 'f') return -1;
3257 int hi = kHexValue[character1];
3258 if (hi == -1) return -1;
3259 if (character2 > 'f') return -1;
3260 int lo = kHexValue[character2];
3261 if (lo == -1) return -1;
3262 return (hi << 4) + lo;
3263}
3264
3265
ager@chromium.org870a0b62008-11-04 11:43:05 +00003266static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003267 int i,
3268 int length,
3269 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003270 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003271 int32_t hi = 0;
3272 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003273 if (character == '%' &&
3274 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003275 source->Get(i + 1) == 'u' &&
3276 (hi = TwoDigitHex(source->Get(i + 2),
3277 source->Get(i + 3))) != -1 &&
3278 (lo = TwoDigitHex(source->Get(i + 4),
3279 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280 *step = 6;
3281 return (hi << 8) + lo;
3282 } else if (character == '%' &&
3283 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003284 (lo = TwoDigitHex(source->Get(i + 1),
3285 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003286 *step = 3;
3287 return lo;
3288 } else {
3289 *step = 1;
3290 return character;
3291 }
3292}
3293
3294
3295static Object* Runtime_URIUnescape(Arguments args) {
3296 NoHandleAllocation ha;
3297 ASSERT(args.length() == 1);
3298 CONVERT_CHECKED(String, source, args[0]);
3299
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003300 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003301
3302 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003303 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003304
3305 int unescaped_length = 0;
3306 for (int i = 0; i < length; unescaped_length++) {
3307 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003308 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003309 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003310 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003311 i += step;
3312 }
3313
3314 // No length change implies no change. Return original string if no change.
3315 if (unescaped_length == length)
3316 return source;
3317
3318 Object* o = ascii ?
3319 Heap::AllocateRawAsciiString(unescaped_length) :
3320 Heap::AllocateRawTwoByteString(unescaped_length);
3321 if (o->IsFailure()) return o;
3322 String* destination = String::cast(o);
3323
3324 int dest_position = 0;
3325 for (int i = 0; i < length; dest_position++) {
3326 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003327 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003328 i += step;
3329 }
3330 return destination;
3331}
3332
3333
3334static Object* Runtime_StringParseInt(Arguments args) {
3335 NoHandleAllocation ha;
3336
3337 CONVERT_CHECKED(String, s, args[0]);
3338 CONVERT_DOUBLE_CHECKED(n, args[1]);
3339 int radix = FastD2I(n);
3340
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003341 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003342
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003343 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003344 int i;
3345
3346 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003347 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003348 if (i == len) return Heap::nan_value();
3349
3350 // Compute the sign (default to +).
3351 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003352 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003353 sign = -1;
3354 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003355 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003356 i++;
3357 }
3358
3359 // Compute the radix if 0.
3360 if (radix == 0) {
3361 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003362 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003363 radix = 8;
3364 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003365 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003366 if (c == 'x' || c == 'X') {
3367 radix = 16;
3368 i += 2;
3369 }
3370 }
3371 }
3372 } else if (radix == 16) {
3373 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003374 if (i + 1 < len && s->Get(i) == '0') {
3375 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003376 if (c == 'x' || c == 'X') i += 2;
3377 }
3378 }
3379
3380 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3381 double value;
3382 int end_index = StringToInt(s, i, radix, &value);
3383 if (end_index != i) {
3384 return Heap::NumberFromDouble(sign * value);
3385 }
3386 return Heap::nan_value();
3387}
3388
3389
3390static Object* Runtime_StringParseFloat(Arguments args) {
3391 NoHandleAllocation ha;
3392 CONVERT_CHECKED(String, str, args[0]);
3393
3394 // ECMA-262 section 15.1.2.3, empty string is NaN
3395 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3396
3397 // Create a number object from the value.
3398 return Heap::NumberFromDouble(value);
3399}
3400
3401
3402static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3403static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3404
3405
3406template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003407static Object* ConvertCaseHelper(String* s,
3408 int length,
3409 int input_string_length,
3410 unibrow::Mapping<Converter, 128>* mapping) {
3411 // We try this twice, once with the assumption that the result is no longer
3412 // than the input and, if that assumption breaks, again with the exact
3413 // length. This may not be pretty, but it is nicer than what was here before
3414 // and I hereby claim my vaffel-is.
3415 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003416 // Allocate the resulting string.
3417 //
3418 // NOTE: This assumes that the upper/lower case of an ascii
3419 // character is also ascii. This is currently the case, but it
3420 // might break in the future if we implement more context and locale
3421 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003422 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003423 ? Heap::AllocateRawAsciiString(length)
3424 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003425 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003426 String* result = String::cast(o);
3427 bool has_changed_character = false;
3428
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003429 // Convert all characters to upper case, assuming that they will fit
3430 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003431 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003432 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003433 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003434 // We can assume that the string is not empty
3435 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003436 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003437 bool has_next = buffer->has_more();
3438 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003439 int char_length = mapping->get(current, next, chars);
3440 if (char_length == 0) {
3441 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003442 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003443 i++;
3444 } else if (char_length == 1) {
3445 // Common case: converting the letter resulted in one character.
3446 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003447 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003448 has_changed_character = true;
3449 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003450 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003451 // We've assumed that the result would be as long as the
3452 // input but here is a character that converts to several
3453 // characters. No matter, we calculate the exact length
3454 // of the result and try the whole thing again.
3455 //
3456 // Note that this leaves room for optimization. We could just
3457 // memcpy what we already have to the result string. Also,
3458 // the result string is the last object allocated we could
3459 // "realloc" it and probably, in the vast majority of cases,
3460 // extend the existing string to be able to hold the full
3461 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003462 int next_length = 0;
3463 if (has_next) {
3464 next_length = mapping->get(next, 0, chars);
3465 if (next_length == 0) next_length = 1;
3466 }
3467 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003468 while (buffer->has_more()) {
3469 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003470 // NOTE: we use 0 as the next character here because, while
3471 // the next character may affect what a character converts to,
3472 // it does not in any case affect the length of what it convert
3473 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003474 int char_length = mapping->get(current, 0, chars);
3475 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003476 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003477 if (current_length > Smi::kMaxValue) {
3478 Top::context()->mark_out_of_memory();
3479 return Failure::OutOfMemoryException();
3480 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003481 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003482 // Try again with the real length.
3483 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003484 } else {
3485 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003486 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003487 i++;
3488 }
3489 has_changed_character = true;
3490 }
3491 current = next;
3492 }
3493 if (has_changed_character) {
3494 return result;
3495 } else {
3496 // If we didn't actually change anything in doing the conversion
3497 // we simple return the result and let the converted string
3498 // become garbage; there is no reason to keep two identical strings
3499 // alive.
3500 return s;
3501 }
3502}
3503
3504
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003505template <class Converter>
3506static Object* ConvertCase(Arguments args,
3507 unibrow::Mapping<Converter, 128>* mapping) {
3508 NoHandleAllocation ha;
3509
3510 CONVERT_CHECKED(String, s, args[0]);
3511 s->TryFlattenIfNotFlat();
3512
3513 int input_string_length = s->length();
3514 // Assume that the string is not empty; we need this assumption later
3515 if (input_string_length == 0) return s;
3516 int length = input_string_length;
3517
3518 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3519 if (answer->IsSmi()) {
3520 // Retry with correct length.
3521 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3522 }
3523 return answer; // This may be a failure.
3524}
3525
3526
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003527static Object* Runtime_StringToLowerCase(Arguments args) {
3528 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3529}
3530
3531
3532static Object* Runtime_StringToUpperCase(Arguments args) {
3533 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3534}
3535
3536
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003537bool Runtime::IsUpperCaseChar(uint16_t ch) {
3538 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3539 int char_length = to_upper_mapping.get(ch, 0, chars);
3540 return char_length == 0;
3541}
3542
3543
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003544static Object* Runtime_NumberToString(Arguments args) {
3545 NoHandleAllocation ha;
3546 ASSERT(args.length() == 1);
3547
3548 Object* number = args[0];
3549 RUNTIME_ASSERT(number->IsNumber());
3550
3551 Object* cached = Heap::GetNumberStringCache(number);
3552 if (cached != Heap::undefined_value()) {
3553 return cached;
3554 }
3555
3556 char arr[100];
3557 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3558 const char* str;
3559 if (number->IsSmi()) {
3560 int num = Smi::cast(number)->value();
3561 str = IntToCString(num, buffer);
3562 } else {
3563 double num = HeapNumber::cast(number)->value();
3564 str = DoubleToCString(num, buffer);
3565 }
3566 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3567
3568 if (!result->IsFailure()) {
3569 Heap::SetNumberStringCache(number, String::cast(result));
3570 }
3571 return result;
3572}
3573
3574
3575static Object* Runtime_NumberToInteger(Arguments args) {
3576 NoHandleAllocation ha;
3577 ASSERT(args.length() == 1);
3578
3579 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003580 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003581 CONVERT_DOUBLE_CHECKED(number, obj);
3582 return Heap::NumberFromDouble(DoubleToInteger(number));
3583}
3584
3585
3586static Object* Runtime_NumberToJSUint32(Arguments args) {
3587 NoHandleAllocation ha;
3588 ASSERT(args.length() == 1);
3589
3590 Object* obj = args[0];
3591 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3592 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3593 return Heap::NumberFromUint32(number);
3594}
3595
3596
3597static Object* Runtime_NumberToJSInt32(Arguments args) {
3598 NoHandleAllocation ha;
3599 ASSERT(args.length() == 1);
3600
3601 Object* obj = args[0];
3602 if (obj->IsSmi()) return obj;
3603 CONVERT_DOUBLE_CHECKED(number, obj);
3604 return Heap::NumberFromInt32(DoubleToInt32(number));
3605}
3606
3607
ager@chromium.org870a0b62008-11-04 11:43:05 +00003608// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3609// a small integer.
3610static Object* Runtime_NumberToSmi(Arguments args) {
3611 NoHandleAllocation ha;
3612 ASSERT(args.length() == 1);
3613
3614 Object* obj = args[0];
3615 if (obj->IsSmi()) {
3616 return obj;
3617 }
3618 if (obj->IsHeapNumber()) {
3619 double value = HeapNumber::cast(obj)->value();
3620 int int_value = FastD2I(value);
3621 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3622 return Smi::FromInt(int_value);
3623 }
3624 }
3625 return Heap::nan_value();
3626}
3627
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003628
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003629static Object* Runtime_NumberAdd(Arguments args) {
3630 NoHandleAllocation ha;
3631 ASSERT(args.length() == 2);
3632
3633 CONVERT_DOUBLE_CHECKED(x, args[0]);
3634 CONVERT_DOUBLE_CHECKED(y, args[1]);
3635 return Heap::AllocateHeapNumber(x + y);
3636}
3637
3638
3639static Object* Runtime_NumberSub(Arguments args) {
3640 NoHandleAllocation ha;
3641 ASSERT(args.length() == 2);
3642
3643 CONVERT_DOUBLE_CHECKED(x, args[0]);
3644 CONVERT_DOUBLE_CHECKED(y, args[1]);
3645 return Heap::AllocateHeapNumber(x - y);
3646}
3647
3648
3649static Object* Runtime_NumberMul(Arguments args) {
3650 NoHandleAllocation ha;
3651 ASSERT(args.length() == 2);
3652
3653 CONVERT_DOUBLE_CHECKED(x, args[0]);
3654 CONVERT_DOUBLE_CHECKED(y, args[1]);
3655 return Heap::AllocateHeapNumber(x * y);
3656}
3657
3658
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003659static Object* Runtime_NumberUnaryMinus(Arguments args) {
3660 NoHandleAllocation ha;
3661 ASSERT(args.length() == 1);
3662
3663 CONVERT_DOUBLE_CHECKED(x, args[0]);
3664 return Heap::AllocateHeapNumber(-x);
3665}
3666
3667
3668static Object* Runtime_NumberDiv(Arguments args) {
3669 NoHandleAllocation ha;
3670 ASSERT(args.length() == 2);
3671
3672 CONVERT_DOUBLE_CHECKED(x, args[0]);
3673 CONVERT_DOUBLE_CHECKED(y, args[1]);
3674 return Heap::NewNumberFromDouble(x / y);
3675}
3676
3677
3678static Object* Runtime_NumberMod(Arguments args) {
3679 NoHandleAllocation ha;
3680 ASSERT(args.length() == 2);
3681
3682 CONVERT_DOUBLE_CHECKED(x, args[0]);
3683 CONVERT_DOUBLE_CHECKED(y, args[1]);
3684
3685#ifdef WIN32
3686 // Workaround MS fmod bugs. ECMA-262 says:
3687 // dividend is finite and divisor is an infinity => result equals dividend
3688 // dividend is a zero and divisor is nonzero finite => result equals dividend
3689 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3690 !(x == 0 && (y != 0 && isfinite(y))))
3691#endif
3692 x = fmod(x, y);
3693 // NewNumberFromDouble may return a Smi instead of a Number object
3694 return Heap::NewNumberFromDouble(x);
3695}
3696
3697
3698static Object* Runtime_StringAdd(Arguments args) {
3699 NoHandleAllocation ha;
3700 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003701 CONVERT_CHECKED(String, str1, args[0]);
3702 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003703 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003704}
3705
3706
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003707template<typename sinkchar>
3708static inline void StringBuilderConcatHelper(String* special,
3709 sinkchar* sink,
3710 FixedArray* fixed_array,
3711 int array_length) {
3712 int position = 0;
3713 for (int i = 0; i < array_length; i++) {
3714 Object* element = fixed_array->get(i);
3715 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003716 int encoded_slice = Smi::cast(element)->value();
3717 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3718 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003719 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003720 sink + position,
3721 pos,
3722 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003723 position += len;
3724 } else {
3725 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003726 int element_length = string->length();
3727 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003728 position += element_length;
3729 }
3730 }
3731}
3732
3733
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003734static Object* Runtime_StringBuilderConcat(Arguments args) {
3735 NoHandleAllocation ha;
3736 ASSERT(args.length() == 2);
3737 CONVERT_CHECKED(JSArray, array, args[0]);
3738 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003739 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003740 Object* smi_array_length = array->length();
3741 if (!smi_array_length->IsSmi()) {
3742 Top::context()->mark_out_of_memory();
3743 return Failure::OutOfMemoryException();
3744 }
3745 int array_length = Smi::cast(smi_array_length)->value();
3746 if (!array->HasFastElements()) {
3747 return Top::Throw(Heap::illegal_argument_symbol());
3748 }
3749 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003750 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003751 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003752 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003753
3754 if (array_length == 0) {
3755 return Heap::empty_string();
3756 } else if (array_length == 1) {
3757 Object* first = fixed_array->get(0);
3758 if (first->IsString()) return first;
3759 }
3760
ager@chromium.org5ec48922009-05-05 07:25:34 +00003761 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003762 int position = 0;
3763 for (int i = 0; i < array_length; i++) {
3764 Object* elt = fixed_array->get(i);
3765 if (elt->IsSmi()) {
3766 int len = Smi::cast(elt)->value();
3767 int pos = len >> 11;
3768 len &= 0x7ff;
3769 if (pos + len > special_length) {
3770 return Top::Throw(Heap::illegal_argument_symbol());
3771 }
3772 position += len;
3773 } else if (elt->IsString()) {
3774 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003775 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003776 if (!Smi::IsValid(element_length + position)) {
3777 Top::context()->mark_out_of_memory();
3778 return Failure::OutOfMemoryException();
3779 }
3780 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003781 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003782 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003783 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003784 } else {
3785 return Top::Throw(Heap::illegal_argument_symbol());
3786 }
3787 }
3788
3789 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003791
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003792 if (ascii) {
3793 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003794 if (object->IsFailure()) return object;
3795 SeqAsciiString* answer = SeqAsciiString::cast(object);
3796 StringBuilderConcatHelper(special,
3797 answer->GetChars(),
3798 fixed_array,
3799 array_length);
3800 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003801 } else {
3802 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003803 if (object->IsFailure()) return object;
3804 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3805 StringBuilderConcatHelper(special,
3806 answer->GetChars(),
3807 fixed_array,
3808 array_length);
3809 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003810 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003811}
3812
3813
3814static Object* Runtime_NumberOr(Arguments args) {
3815 NoHandleAllocation ha;
3816 ASSERT(args.length() == 2);
3817
3818 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3819 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3820 return Heap::NumberFromInt32(x | y);
3821}
3822
3823
3824static Object* Runtime_NumberAnd(Arguments args) {
3825 NoHandleAllocation ha;
3826 ASSERT(args.length() == 2);
3827
3828 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3829 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3830 return Heap::NumberFromInt32(x & y);
3831}
3832
3833
3834static Object* Runtime_NumberXor(Arguments args) {
3835 NoHandleAllocation ha;
3836 ASSERT(args.length() == 2);
3837
3838 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3839 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3840 return Heap::NumberFromInt32(x ^ y);
3841}
3842
3843
3844static Object* Runtime_NumberNot(Arguments args) {
3845 NoHandleAllocation ha;
3846 ASSERT(args.length() == 1);
3847
3848 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3849 return Heap::NumberFromInt32(~x);
3850}
3851
3852
3853static Object* Runtime_NumberShl(Arguments args) {
3854 NoHandleAllocation ha;
3855 ASSERT(args.length() == 2);
3856
3857 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3858 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3859 return Heap::NumberFromInt32(x << (y & 0x1f));
3860}
3861
3862
3863static Object* Runtime_NumberShr(Arguments args) {
3864 NoHandleAllocation ha;
3865 ASSERT(args.length() == 2);
3866
3867 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3868 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3869 return Heap::NumberFromUint32(x >> (y & 0x1f));
3870}
3871
3872
3873static Object* Runtime_NumberSar(Arguments args) {
3874 NoHandleAllocation ha;
3875 ASSERT(args.length() == 2);
3876
3877 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3878 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3879 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3880}
3881
3882
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003883static Object* Runtime_NumberEquals(Arguments args) {
3884 NoHandleAllocation ha;
3885 ASSERT(args.length() == 2);
3886
3887 CONVERT_DOUBLE_CHECKED(x, args[0]);
3888 CONVERT_DOUBLE_CHECKED(y, args[1]);
3889 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3890 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3891 if (x == y) return Smi::FromInt(EQUAL);
3892 Object* result;
3893 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3894 result = Smi::FromInt(EQUAL);
3895 } else {
3896 result = Smi::FromInt(NOT_EQUAL);
3897 }
3898 return result;
3899}
3900
3901
3902static Object* Runtime_StringEquals(Arguments args) {
3903 NoHandleAllocation ha;
3904 ASSERT(args.length() == 2);
3905
3906 CONVERT_CHECKED(String, x, args[0]);
3907 CONVERT_CHECKED(String, y, args[1]);
3908
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003909 bool not_equal = !x->Equals(y);
3910 // This is slightly convoluted because the value that signifies
3911 // equality is 0 and inequality is 1 so we have to negate the result
3912 // from String::Equals.
3913 ASSERT(not_equal == 0 || not_equal == 1);
3914 STATIC_CHECK(EQUAL == 0);
3915 STATIC_CHECK(NOT_EQUAL == 1);
3916 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917}
3918
3919
3920static Object* Runtime_NumberCompare(Arguments args) {
3921 NoHandleAllocation ha;
3922 ASSERT(args.length() == 3);
3923
3924 CONVERT_DOUBLE_CHECKED(x, args[0]);
3925 CONVERT_DOUBLE_CHECKED(y, args[1]);
3926 if (isnan(x) || isnan(y)) return args[2];
3927 if (x == y) return Smi::FromInt(EQUAL);
3928 if (isless(x, y)) return Smi::FromInt(LESS);
3929 return Smi::FromInt(GREATER);
3930}
3931
3932
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003933// Compare two Smis as if they were converted to strings and then
3934// compared lexicographically.
3935static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3936 NoHandleAllocation ha;
3937 ASSERT(args.length() == 2);
3938
3939 // Arrays for the individual characters of the two Smis. Smis are
3940 // 31 bit integers and 10 decimal digits are therefore enough.
3941 static int x_elms[10];
3942 static int y_elms[10];
3943
3944 // Extract the integer values from the Smis.
3945 CONVERT_CHECKED(Smi, x, args[0]);
3946 CONVERT_CHECKED(Smi, y, args[1]);
3947 int x_value = x->value();
3948 int y_value = y->value();
3949
3950 // If the integers are equal so are the string representations.
3951 if (x_value == y_value) return Smi::FromInt(EQUAL);
3952
3953 // If one of the integers are zero the normal integer order is the
3954 // same as the lexicographic order of the string representations.
3955 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3956
ager@chromium.org32912102009-01-16 10:38:43 +00003957 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003958 // smallest because the char code of '-' is less than the char code
3959 // of any digit. Otherwise, we make both values positive.
3960 if (x_value < 0 || y_value < 0) {
3961 if (y_value >= 0) return Smi::FromInt(LESS);
3962 if (x_value >= 0) return Smi::FromInt(GREATER);
3963 x_value = -x_value;
3964 y_value = -y_value;
3965 }
3966
3967 // Convert the integers to arrays of their decimal digits.
3968 int x_index = 0;
3969 int y_index = 0;
3970 while (x_value > 0) {
3971 x_elms[x_index++] = x_value % 10;
3972 x_value /= 10;
3973 }
3974 while (y_value > 0) {
3975 y_elms[y_index++] = y_value % 10;
3976 y_value /= 10;
3977 }
3978
3979 // Loop through the arrays of decimal digits finding the first place
3980 // where they differ.
3981 while (--x_index >= 0 && --y_index >= 0) {
3982 int diff = x_elms[x_index] - y_elms[y_index];
3983 if (diff != 0) return Smi::FromInt(diff);
3984 }
3985
3986 // If one array is a suffix of the other array, the longest array is
3987 // the representation of the largest of the Smis in the
3988 // lexicographic ordering.
3989 return Smi::FromInt(x_index - y_index);
3990}
3991
3992
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003993static Object* Runtime_StringCompare(Arguments args) {
3994 NoHandleAllocation ha;
3995 ASSERT(args.length() == 2);
3996
3997 CONVERT_CHECKED(String, x, args[0]);
3998 CONVERT_CHECKED(String, y, args[1]);
3999
4000 // A few fast case tests before we flatten.
4001 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004002 if (y->length() == 0) {
4003 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004004 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004005 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004006 return Smi::FromInt(LESS);
4007 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004008
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004009 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004010 if (d < 0) return Smi::FromInt(LESS);
4011 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004012
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004013 x->TryFlattenIfNotFlat();
4014 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004015
4016 static StringInputBuffer bufx;
4017 static StringInputBuffer bufy;
4018 bufx.Reset(x);
4019 bufy.Reset(y);
4020 while (bufx.has_more() && bufy.has_more()) {
4021 int d = bufx.GetNext() - bufy.GetNext();
4022 if (d < 0) return Smi::FromInt(LESS);
4023 else if (d > 0) return Smi::FromInt(GREATER);
4024 }
4025
4026 // x is (non-trivial) prefix of y:
4027 if (bufy.has_more()) return Smi::FromInt(LESS);
4028 // y is prefix of x:
4029 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4030}
4031
4032
4033static Object* Runtime_Math_abs(Arguments args) {
4034 NoHandleAllocation ha;
4035 ASSERT(args.length() == 1);
4036
4037 CONVERT_DOUBLE_CHECKED(x, args[0]);
4038 return Heap::AllocateHeapNumber(fabs(x));
4039}
4040
4041
4042static Object* Runtime_Math_acos(Arguments args) {
4043 NoHandleAllocation ha;
4044 ASSERT(args.length() == 1);
4045
4046 CONVERT_DOUBLE_CHECKED(x, args[0]);
4047 return Heap::AllocateHeapNumber(acos(x));
4048}
4049
4050
4051static Object* Runtime_Math_asin(Arguments args) {
4052 NoHandleAllocation ha;
4053 ASSERT(args.length() == 1);
4054
4055 CONVERT_DOUBLE_CHECKED(x, args[0]);
4056 return Heap::AllocateHeapNumber(asin(x));
4057}
4058
4059
4060static Object* Runtime_Math_atan(Arguments args) {
4061 NoHandleAllocation ha;
4062 ASSERT(args.length() == 1);
4063
4064 CONVERT_DOUBLE_CHECKED(x, args[0]);
4065 return Heap::AllocateHeapNumber(atan(x));
4066}
4067
4068
4069static Object* Runtime_Math_atan2(Arguments args) {
4070 NoHandleAllocation ha;
4071 ASSERT(args.length() == 2);
4072
4073 CONVERT_DOUBLE_CHECKED(x, args[0]);
4074 CONVERT_DOUBLE_CHECKED(y, args[1]);
4075 double result;
4076 if (isinf(x) && isinf(y)) {
4077 // Make sure that the result in case of two infinite arguments
4078 // is a multiple of Pi / 4. The sign of the result is determined
4079 // by the first argument (x) and the sign of the second argument
4080 // determines the multiplier: one or three.
4081 static double kPiDividedBy4 = 0.78539816339744830962;
4082 int multiplier = (x < 0) ? -1 : 1;
4083 if (y < 0) multiplier *= 3;
4084 result = multiplier * kPiDividedBy4;
4085 } else {
4086 result = atan2(x, y);
4087 }
4088 return Heap::AllocateHeapNumber(result);
4089}
4090
4091
4092static Object* Runtime_Math_ceil(Arguments args) {
4093 NoHandleAllocation ha;
4094 ASSERT(args.length() == 1);
4095
4096 CONVERT_DOUBLE_CHECKED(x, args[0]);
4097 return Heap::NumberFromDouble(ceiling(x));
4098}
4099
4100
4101static Object* Runtime_Math_cos(Arguments args) {
4102 NoHandleAllocation ha;
4103 ASSERT(args.length() == 1);
4104
4105 CONVERT_DOUBLE_CHECKED(x, args[0]);
4106 return Heap::AllocateHeapNumber(cos(x));
4107}
4108
4109
4110static Object* Runtime_Math_exp(Arguments args) {
4111 NoHandleAllocation ha;
4112 ASSERT(args.length() == 1);
4113
4114 CONVERT_DOUBLE_CHECKED(x, args[0]);
4115 return Heap::AllocateHeapNumber(exp(x));
4116}
4117
4118
4119static Object* Runtime_Math_floor(Arguments args) {
4120 NoHandleAllocation ha;
4121 ASSERT(args.length() == 1);
4122
4123 CONVERT_DOUBLE_CHECKED(x, args[0]);
4124 return Heap::NumberFromDouble(floor(x));
4125}
4126
4127
4128static Object* Runtime_Math_log(Arguments args) {
4129 NoHandleAllocation ha;
4130 ASSERT(args.length() == 1);
4131
4132 CONVERT_DOUBLE_CHECKED(x, args[0]);
4133 return Heap::AllocateHeapNumber(log(x));
4134}
4135
4136
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004137// Helper function to compute x^y, where y is known to be an
4138// integer. Uses binary decomposition to limit the number of
4139// multiplications; see the discussion in "Hacker's Delight" by Henry
4140// S. Warren, Jr., figure 11-6, page 213.
4141static double powi(double x, int y) {
4142 ASSERT(y != kMinInt);
4143 unsigned n = (y < 0) ? -y : y;
4144 double m = x;
4145 double p = 1;
4146 while (true) {
4147 if ((n & 1) != 0) p *= m;
4148 n >>= 1;
4149 if (n == 0) {
4150 if (y < 0) {
4151 // Unfortunately, we have to be careful when p has reached
4152 // infinity in the computation, because sometimes the higher
4153 // internal precision in the pow() implementation would have
4154 // given us a finite p. This happens very rarely.
4155 double result = 1.0 / p;
4156 return (result == 0 && isinf(p))
4157 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4158 : result;
4159 } else {
4160 return p;
4161 }
4162 }
4163 m *= m;
4164 }
4165}
4166
4167
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004168static Object* Runtime_Math_pow(Arguments args) {
4169 NoHandleAllocation ha;
4170 ASSERT(args.length() == 2);
4171
4172 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004173
4174 // If the second argument is a smi, it is much faster to call the
4175 // custom powi() function than the generic pow().
4176 if (args[1]->IsSmi()) {
4177 int y = Smi::cast(args[1])->value();
4178 return Heap::AllocateHeapNumber(powi(x, y));
4179 }
4180
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004181 CONVERT_DOUBLE_CHECKED(y, args[1]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004182 if (y == 0.5) {
4183 // It's not uncommon to use Math.pow(x, 0.5) to compute the square
4184 // root of a number. To speed up such computations, we explictly
4185 // check for this case and use the sqrt() function which is faster
4186 // than pow().
4187 return Heap::AllocateHeapNumber(sqrt(x));
4188 } else if (y == -0.5) {
4189 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4190 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004191 } else if (y == 0) {
4192 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004193 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4194 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004195 } else {
4196 return Heap::AllocateHeapNumber(pow(x, y));
4197 }
4198}
4199
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004200
4201static Object* Runtime_Math_round(Arguments args) {
4202 NoHandleAllocation ha;
4203 ASSERT(args.length() == 1);
4204
4205 CONVERT_DOUBLE_CHECKED(x, args[0]);
4206 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4207 return Heap::NumberFromDouble(floor(x + 0.5));
4208}
4209
4210
4211static Object* Runtime_Math_sin(Arguments args) {
4212 NoHandleAllocation ha;
4213 ASSERT(args.length() == 1);
4214
4215 CONVERT_DOUBLE_CHECKED(x, args[0]);
4216 return Heap::AllocateHeapNumber(sin(x));
4217}
4218
4219
4220static Object* Runtime_Math_sqrt(Arguments args) {
4221 NoHandleAllocation ha;
4222 ASSERT(args.length() == 1);
4223
4224 CONVERT_DOUBLE_CHECKED(x, args[0]);
4225 return Heap::AllocateHeapNumber(sqrt(x));
4226}
4227
4228
4229static Object* Runtime_Math_tan(Arguments args) {
4230 NoHandleAllocation ha;
4231 ASSERT(args.length() == 1);
4232
4233 CONVERT_DOUBLE_CHECKED(x, args[0]);
4234 return Heap::AllocateHeapNumber(tan(x));
4235}
4236
4237
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004238// The NewArguments function is only used when constructing the
4239// arguments array when calling non-functions from JavaScript in
4240// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004241static Object* Runtime_NewArguments(Arguments args) {
4242 NoHandleAllocation ha;
4243 ASSERT(args.length() == 1);
4244
4245 // ECMA-262, 3rd., 10.1.8, p.39
4246 CONVERT_CHECKED(JSFunction, callee, args[0]);
4247
4248 // Compute the frame holding the arguments.
4249 JavaScriptFrameIterator it;
4250 it.AdvanceToArgumentsFrame();
4251 JavaScriptFrame* frame = it.frame();
4252
4253 const int length = frame->GetProvidedParametersCount();
4254 Object* result = Heap::AllocateArgumentsObject(callee, length);
4255 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004256 if (length > 0) {
4257 Object* obj = Heap::AllocateFixedArray(length);
4258 if (obj->IsFailure()) return obj;
4259 FixedArray* array = FixedArray::cast(obj);
4260 ASSERT(array->length() == length);
4261 WriteBarrierMode mode = array->GetWriteBarrierMode();
4262 for (int i = 0; i < length; i++) {
4263 array->set(i, frame->GetParameter(i), mode);
4264 }
4265 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004266 }
4267 return result;
4268}
4269
4270
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004271static Object* Runtime_NewArgumentsFast(Arguments args) {
4272 NoHandleAllocation ha;
4273 ASSERT(args.length() == 3);
4274
4275 JSFunction* callee = JSFunction::cast(args[0]);
4276 Object** parameters = reinterpret_cast<Object**>(args[1]);
4277 const int length = Smi::cast(args[2])->value();
4278
4279 Object* result = Heap::AllocateArgumentsObject(callee, length);
4280 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004281 ASSERT(Heap::InNewSpace(result));
4282
4283 // Allocate the elements if needed.
4284 if (length > 0) {
4285 // Allocate the fixed array.
4286 Object* obj = Heap::AllocateRawFixedArray(length);
4287 if (obj->IsFailure()) return obj;
4288 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4289 FixedArray* array = FixedArray::cast(obj);
4290 array->set_length(length);
4291 WriteBarrierMode mode = array->GetWriteBarrierMode();
4292 for (int i = 0; i < length; i++) {
4293 array->set(i, *--parameters, mode);
4294 }
4295 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4296 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004297 }
4298 return result;
4299}
4300
4301
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004302static Object* Runtime_NewClosure(Arguments args) {
4303 HandleScope scope;
4304 ASSERT(args.length() == 2);
4305 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4306 CONVERT_ARG_CHECKED(Context, context, 1);
4307
4308 Handle<JSFunction> result =
4309 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4310 return *result;
4311}
4312
4313
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004314static Handle<Code> ComputeConstructStub(Handle<Map> map) {
4315 // TODO(385): Change this to create a construct stub specialized for
4316 // the given map to make allocation of simple objects - and maybe
4317 // arrays - much faster.
4318 return Handle<Code>(Builtins::builtin(Builtins::JSConstructStubGeneric));
4319}
4320
4321
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004322static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004323 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004324 ASSERT(args.length() == 1);
4325
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004326 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004327
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004328 // If the constructor isn't a proper function we throw a type error.
4329 if (!constructor->IsJSFunction()) {
4330 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4331 Handle<Object> type_error =
4332 Factory::NewTypeError("not_constructor", arguments);
4333 return Top::Throw(*type_error);
4334 }
4335
4336 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004337#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004338 // Handle stepping into constructors if step into is active.
4339 if (Debug::StepInActive()) {
4340 Debug::HandleStepIn(function, 0, true);
4341 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004342#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004343
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004344 if (function->has_initial_map()) {
4345 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004346 // The 'Function' function ignores the receiver object when
4347 // called using 'new' and creates a new JSFunction object that
4348 // is returned. The receiver object is only used for error
4349 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004350 // JSFunction. Factory::NewJSObject() should not be used to
4351 // allocate JSFunctions since it does not properly initialize
4352 // the shared part of the function. Since the receiver is
4353 // ignored anyway, we use the global object as the receiver
4354 // instead of a new JSFunction object. This way, errors are
4355 // reported the same way whether or not 'Function' is called
4356 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004357 return Top::context()->global();
4358 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004359 }
4360
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004361 bool first_allocation = !function->has_initial_map();
4362 Handle<JSObject> result = Factory::NewJSObject(function);
4363 if (first_allocation) {
4364 Handle<Map> map = Handle<Map>(function->initial_map());
4365 Handle<Code> stub = ComputeConstructStub(map);
4366 function->shared()->set_construct_stub(*stub);
4367 }
4368 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004369}
4370
4371
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004372static Object* Runtime_LazyCompile(Arguments args) {
4373 HandleScope scope;
4374 ASSERT(args.length() == 1);
4375
4376 Handle<JSFunction> function = args.at<JSFunction>(0);
4377#ifdef DEBUG
4378 if (FLAG_trace_lazy) {
4379 PrintF("[lazy: ");
4380 function->shared()->name()->Print();
4381 PrintF("]\n");
4382 }
4383#endif
4384
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004385 // Compile the target function. Here we compile using CompileLazyInLoop in
4386 // order to get the optimized version. This helps code like delta-blue
4387 // that calls performance-critical routines through constructors. A
4388 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4389 // direct call. Since the in-loop tracking takes place through CallICs
4390 // this means that things called through constructors are never known to
4391 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004392 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004393 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004394 return Failure::Exception();
4395 }
4396
4397 return function->code();
4398}
4399
4400
4401static Object* Runtime_GetCalledFunction(Arguments args) {
4402 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004403 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004404 StackFrameIterator it;
4405 // Get past the JS-to-C exit frame.
4406 ASSERT(it.frame()->is_exit());
4407 it.Advance();
4408 // Get past the CALL_NON_FUNCTION activation frame.
4409 ASSERT(it.frame()->is_java_script());
4410 it.Advance();
4411 // Argument adaptor frames do not copy the function; we have to skip
4412 // past them to get to the real calling frame.
4413 if (it.frame()->is_arguments_adaptor()) it.Advance();
4414 // Get the function from the top of the expression stack of the
4415 // calling frame.
4416 StandardFrame* frame = StandardFrame::cast(it.frame());
4417 int index = frame->ComputeExpressionsCount() - 1;
4418 Object* result = frame->GetExpression(index);
4419 return result;
4420}
4421
4422
4423static Object* Runtime_GetFunctionDelegate(Arguments args) {
4424 HandleScope scope;
4425 ASSERT(args.length() == 1);
4426 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4427 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4428}
4429
4430
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004431static Object* Runtime_GetConstructorDelegate(Arguments args) {
4432 HandleScope scope;
4433 ASSERT(args.length() == 1);
4434 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4435 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4436}
4437
4438
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439static Object* Runtime_NewContext(Arguments args) {
4440 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004441 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004442
kasper.lund7276f142008-07-30 08:49:36 +00004443 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004444 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4445 Object* result = Heap::AllocateFunctionContext(length, function);
4446 if (result->IsFailure()) return result;
4447
4448 Top::set_context(Context::cast(result));
4449
kasper.lund7276f142008-07-30 08:49:36 +00004450 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004451}
4452
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004453static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004454 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004455 Object* js_object = object;
4456 if (!js_object->IsJSObject()) {
4457 js_object = js_object->ToObject();
4458 if (js_object->IsFailure()) {
4459 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004460 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004461 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004462 Handle<Object> result =
4463 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4464 return Top::Throw(*result);
4465 }
4466 }
4467
4468 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004469 Heap::AllocateWithContext(Top::context(),
4470 JSObject::cast(js_object),
4471 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004472 if (result->IsFailure()) return result;
4473
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004474 Context* context = Context::cast(result);
4475 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004476
kasper.lund7276f142008-07-30 08:49:36 +00004477 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004478}
4479
4480
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004481static Object* Runtime_PushContext(Arguments args) {
4482 NoHandleAllocation ha;
4483 ASSERT(args.length() == 1);
4484 return PushContextHelper(args[0], false);
4485}
4486
4487
4488static Object* Runtime_PushCatchContext(Arguments args) {
4489 NoHandleAllocation ha;
4490 ASSERT(args.length() == 1);
4491 return PushContextHelper(args[0], true);
4492}
4493
4494
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004495static Object* Runtime_LookupContext(Arguments args) {
4496 HandleScope scope;
4497 ASSERT(args.length() == 2);
4498
4499 CONVERT_ARG_CHECKED(Context, context, 0);
4500 CONVERT_ARG_CHECKED(String, name, 1);
4501
4502 int index;
4503 PropertyAttributes attributes;
4504 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004505 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004506 context->Lookup(name, flags, &index, &attributes);
4507
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004508 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004509 ASSERT(holder->IsJSObject());
4510 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004511 }
4512
4513 // No intermediate context found. Use global object by default.
4514 return Top::context()->global();
4515}
4516
4517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004518// A mechanism to return pairs of Object*'s. This is somewhat
4519// compiler-dependent as it assumes that a 64-bit value (a long long)
4520// is returned via two registers (edx:eax on ia32). Both the ia32 and
4521// arm platform support this; it is mostly an issue of "coaxing" the
4522// compiler to do the right thing.
4523//
4524// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004525// TODO(x64): Definitely!
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004526typedef uint64_t ObjectPair;
4527static inline ObjectPair MakePair(Object* x, Object* y) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004528#if V8_HOST_ARCH_64_BIT
4529 UNIMPLEMENTED();
4530 return 0;
4531#else
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004532 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004533 (reinterpret_cast<ObjectPair>(y) << 32);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004534#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004535}
4536
4537
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004538static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004539 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4540 USE(attributes);
4541 return x->IsTheHole() ? Heap::undefined_value() : x;
4542}
4543
4544
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004545static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4546 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004547 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004548 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004549 JSFunction* context_extension_function =
4550 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004551 // If the holder isn't a context extension object, we just return it
4552 // as the receiver. This allows arguments objects to be used as
4553 // receivers, but only if they are put in the context scope chain
4554 // explicitly via a with-statement.
4555 Object* constructor = holder->map()->constructor();
4556 if (constructor != context_extension_function) return holder;
4557 // Fall back to using the global object as the receiver if the
4558 // property turns out to be a local variable allocated in a context
4559 // extension object - introduced via eval.
4560 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004561}
4562
4563
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004564static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004565 HandleScope scope;
4566 ASSERT(args.length() == 2);
4567
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004568 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004569 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004570 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004571 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004572 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004573
4574 int index;
4575 PropertyAttributes attributes;
4576 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004577 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004578 context->Lookup(name, flags, &index, &attributes);
4579
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004580 // If the index is non-negative, the slot has been found in a local
4581 // variable or a parameter. Read it from the context object or the
4582 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004583 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004584 // If the "property" we were looking for is a local variable or an
4585 // argument in a context, the receiver is the global object; see
4586 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4587 JSObject* receiver = Top::context()->global()->global_receiver();
4588 Object* value = (holder->IsContext())
4589 ? Context::cast(*holder)->get(index)
4590 : JSObject::cast(*holder)->GetElement(index);
4591 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004592 }
4593
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004594 // If the holder is found, we read the property from it.
4595 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004596 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004597 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004598 JSObject* receiver;
4599 if (object->IsGlobalObject()) {
4600 receiver = GlobalObject::cast(object)->global_receiver();
4601 } else if (context->is_exception_holder(*holder)) {
4602 receiver = Top::context()->global()->global_receiver();
4603 } else {
4604 receiver = ComputeReceiverForNonGlobal(object);
4605 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004606 // No need to unhole the value here. This is taken care of by the
4607 // GetProperty function.
4608 Object* value = object->GetProperty(*name);
4609 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004610 }
4611
4612 if (throw_error) {
4613 // The property doesn't exist - throw exception.
4614 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004615 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004616 return MakePair(Top::Throw(*reference_error), NULL);
4617 } else {
4618 // The property doesn't exist - return undefined
4619 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4620 }
4621}
4622
4623
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004624static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004625 return LoadContextSlotHelper(args, true);
4626}
4627
4628
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004629static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004630 return LoadContextSlotHelper(args, false);
4631}
4632
4633
4634static Object* Runtime_StoreContextSlot(Arguments args) {
4635 HandleScope scope;
4636 ASSERT(args.length() == 3);
4637
4638 Handle<Object> value(args[0]);
4639 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004640 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004641
4642 int index;
4643 PropertyAttributes attributes;
4644 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004645 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004646 context->Lookup(name, flags, &index, &attributes);
4647
4648 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004649 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004650 // Ignore if read_only variable.
4651 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004652 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004653 }
4654 } else {
4655 ASSERT((attributes & READ_ONLY) == 0);
4656 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004657 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004658 USE(result);
4659 ASSERT(!result->IsFailure());
4660 }
4661 return *value;
4662 }
4663
4664 // Slow case: The property is not in a FixedArray context.
4665 // It is either in an JSObject extension context or it was not found.
4666 Handle<JSObject> context_ext;
4667
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004668 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004669 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004670 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004671 } else {
4672 // The property was not found. It needs to be stored in the global context.
4673 ASSERT(attributes == ABSENT);
4674 attributes = NONE;
4675 context_ext = Handle<JSObject>(Top::context()->global());
4676 }
4677
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004678 // Set the property, but ignore if read_only variable on the context
4679 // extension object itself.
4680 if ((attributes & READ_ONLY) == 0 ||
4681 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004682 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4683 if (set.is_null()) {
4684 // Failure::Exception is converted to a null handle in the
4685 // handle-based methods such as SetProperty. We therefore need
4686 // to convert null handles back to exceptions.
4687 ASSERT(Top::has_pending_exception());
4688 return Failure::Exception();
4689 }
4690 }
4691 return *value;
4692}
4693
4694
4695static Object* Runtime_Throw(Arguments args) {
4696 HandleScope scope;
4697 ASSERT(args.length() == 1);
4698
4699 return Top::Throw(args[0]);
4700}
4701
4702
4703static Object* Runtime_ReThrow(Arguments args) {
4704 HandleScope scope;
4705 ASSERT(args.length() == 1);
4706
4707 return Top::ReThrow(args[0]);
4708}
4709
4710
4711static Object* Runtime_ThrowReferenceError(Arguments args) {
4712 HandleScope scope;
4713 ASSERT(args.length() == 1);
4714
4715 Handle<Object> name(args[0]);
4716 Handle<Object> reference_error =
4717 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4718 return Top::Throw(*reference_error);
4719}
4720
4721
4722static Object* Runtime_StackOverflow(Arguments args) {
4723 NoHandleAllocation na;
4724 return Top::StackOverflow();
4725}
4726
4727
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004728static Object* Runtime_StackGuard(Arguments args) {
4729 ASSERT(args.length() == 1);
4730
4731 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004732 if (StackGuard::IsStackOverflow()) {
4733 return Runtime_StackOverflow(args);
4734 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004735
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004736 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004737}
4738
4739
4740// NOTE: These PrintXXX functions are defined for all builds (not just
4741// DEBUG builds) because we may want to be able to trace function
4742// calls in all modes.
4743static void PrintString(String* str) {
4744 // not uncommon to have empty strings
4745 if (str->length() > 0) {
4746 SmartPointer<char> s =
4747 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4748 PrintF("%s", *s);
4749 }
4750}
4751
4752
4753static void PrintObject(Object* obj) {
4754 if (obj->IsSmi()) {
4755 PrintF("%d", Smi::cast(obj)->value());
4756 } else if (obj->IsString() || obj->IsSymbol()) {
4757 PrintString(String::cast(obj));
4758 } else if (obj->IsNumber()) {
4759 PrintF("%g", obj->Number());
4760 } else if (obj->IsFailure()) {
4761 PrintF("<failure>");
4762 } else if (obj->IsUndefined()) {
4763 PrintF("<undefined>");
4764 } else if (obj->IsNull()) {
4765 PrintF("<null>");
4766 } else if (obj->IsTrue()) {
4767 PrintF("<true>");
4768 } else if (obj->IsFalse()) {
4769 PrintF("<false>");
4770 } else {
4771 PrintF("%p", obj);
4772 }
4773}
4774
4775
4776static int StackSize() {
4777 int n = 0;
4778 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4779 return n;
4780}
4781
4782
4783static void PrintTransition(Object* result) {
4784 // indentation
4785 { const int nmax = 80;
4786 int n = StackSize();
4787 if (n <= nmax)
4788 PrintF("%4d:%*s", n, n, "");
4789 else
4790 PrintF("%4d:%*s", n, nmax, "...");
4791 }
4792
4793 if (result == NULL) {
4794 // constructor calls
4795 JavaScriptFrameIterator it;
4796 JavaScriptFrame* frame = it.frame();
4797 if (frame->IsConstructor()) PrintF("new ");
4798 // function name
4799 Object* fun = frame->function();
4800 if (fun->IsJSFunction()) {
4801 PrintObject(JSFunction::cast(fun)->shared()->name());
4802 } else {
4803 PrintObject(fun);
4804 }
4805 // function arguments
4806 // (we are intentionally only printing the actually
4807 // supplied parameters, not all parameters required)
4808 PrintF("(this=");
4809 PrintObject(frame->receiver());
4810 const int length = frame->GetProvidedParametersCount();
4811 for (int i = 0; i < length; i++) {
4812 PrintF(", ");
4813 PrintObject(frame->GetParameter(i));
4814 }
4815 PrintF(") {\n");
4816
4817 } else {
4818 // function result
4819 PrintF("} -> ");
4820 PrintObject(result);
4821 PrintF("\n");
4822 }
4823}
4824
4825
4826static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004827 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004828 NoHandleAllocation ha;
4829 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004830 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004831}
4832
4833
4834static Object* Runtime_TraceExit(Arguments args) {
4835 NoHandleAllocation ha;
4836 PrintTransition(args[0]);
4837 return args[0]; // return TOS
4838}
4839
4840
4841static Object* Runtime_DebugPrint(Arguments args) {
4842 NoHandleAllocation ha;
4843 ASSERT(args.length() == 1);
4844
4845#ifdef DEBUG
4846 if (args[0]->IsString()) {
4847 // If we have a string, assume it's a code "marker"
4848 // and print some interesting cpu debugging info.
4849 JavaScriptFrameIterator it;
4850 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004851 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4852 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853 } else {
4854 PrintF("DebugPrint: ");
4855 }
4856 args[0]->Print();
4857#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004858 // ShortPrint is available in release mode. Print is not.
4859 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860#endif
4861 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004862 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004863
4864 return args[0]; // return TOS
4865}
4866
4867
4868static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004869 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870 NoHandleAllocation ha;
4871 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004872 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004873}
4874
4875
mads.s.ager31e71382008-08-13 09:32:07 +00004876static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004877 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004878 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004879
4880 // According to ECMA-262, section 15.9.1, page 117, the precision of
4881 // the number in a Date object representing a particular instant in
4882 // time is milliseconds. Therefore, we floor the result of getting
4883 // the OS time.
4884 double millis = floor(OS::TimeCurrentMillis());
4885 return Heap::NumberFromDouble(millis);
4886}
4887
4888
4889static Object* Runtime_DateParseString(Arguments args) {
4890 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004891 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004893 CONVERT_ARG_CHECKED(String, str, 0);
4894 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004896 CONVERT_ARG_CHECKED(JSArray, output, 1);
4897 RUNTIME_ASSERT(output->HasFastElements());
4898
4899 AssertNoAllocation no_allocation;
4900
4901 FixedArray* output_array = output->elements();
4902 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4903 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004904 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004905 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004906 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004907 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004908 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4909 }
4910
4911 if (result) {
4912 return *output;
4913 } else {
4914 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004915 }
4916}
4917
4918
4919static Object* Runtime_DateLocalTimezone(Arguments args) {
4920 NoHandleAllocation ha;
4921 ASSERT(args.length() == 1);
4922
4923 CONVERT_DOUBLE_CHECKED(x, args[0]);
4924 char* zone = OS::LocalTimezone(x);
4925 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4926}
4927
4928
4929static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4930 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004931 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004932
4933 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4934}
4935
4936
4937static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4938 NoHandleAllocation ha;
4939 ASSERT(args.length() == 1);
4940
4941 CONVERT_DOUBLE_CHECKED(x, args[0]);
4942 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4943}
4944
4945
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004946static Object* Runtime_NumberIsFinite(Arguments args) {
4947 NoHandleAllocation ha;
4948 ASSERT(args.length() == 1);
4949
4950 CONVERT_DOUBLE_CHECKED(value, args[0]);
4951 Object* result;
4952 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4953 result = Heap::false_value();
4954 } else {
4955 result = Heap::true_value();
4956 }
4957 return result;
4958}
4959
4960
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004961static Object* Runtime_GlobalReceiver(Arguments args) {
4962 ASSERT(args.length() == 1);
4963 Object* global = args[0];
4964 if (!global->IsJSGlobalObject()) return Heap::null_value();
4965 return JSGlobalObject::cast(global)->global_receiver();
4966}
4967
4968
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004969static Object* Runtime_CompileString(Arguments args) {
4970 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004971 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004972 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004973 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004974
ager@chromium.org381abbb2009-02-25 13:23:22 +00004975 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004976 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004977 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4978 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004979 true,
4980 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004981 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004982 Handle<JSFunction> fun =
4983 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4984 return *fun;
4985}
4986
4987
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004988static Handle<JSFunction> GetBuiltinFunction(String* name) {
4989 LookupResult result;
4990 Top::global_context()->builtins()->LocalLookup(name, &result);
4991 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4992}
4993
4994
4995static Object* CompileDirectEval(Handle<String> source) {
4996 // Compute the eval context.
4997 HandleScope scope;
4998 StackFrameLocator locator;
4999 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5000 Handle<Context> context(Context::cast(frame->context()));
5001 bool is_global = context->IsGlobalContext();
5002
ager@chromium.org381abbb2009-02-25 13:23:22 +00005003 // Compile source string in the current context.
5004 Handle<JSFunction> boilerplate =
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005005 Compiler::CompileEval(source, context, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005006 if (boilerplate.is_null()) return Failure::Exception();
5007 Handle<JSFunction> fun =
5008 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5009 return *fun;
5010}
5011
5012
5013static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5014 ASSERT(args.length() == 2);
5015
5016 HandleScope scope;
5017
5018 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5019
5020 Handle<Object> receiver;
5021
5022 // Find where the 'eval' symbol is bound. It is unaliased only if
5023 // it is bound in the global context.
5024 StackFrameLocator locator;
5025 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5026 Handle<Context> context(Context::cast(frame->context()));
5027 int index;
5028 PropertyAttributes attributes;
5029 while (!context.is_null()) {
5030 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5031 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005032 // Stop search when eval is found or when the global context is
5033 // reached.
5034 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005035 if (context->is_function_context()) {
5036 context = Handle<Context>(Context::cast(context->closure()->context()));
5037 } else {
5038 context = Handle<Context>(context->previous());
5039 }
5040 }
5041
iposva@chromium.org245aa852009-02-10 00:49:54 +00005042 // If eval could not be resolved, it has been deleted and we need to
5043 // throw a reference error.
5044 if (attributes == ABSENT) {
5045 Handle<Object> name = Factory::eval_symbol();
5046 Handle<Object> reference_error =
5047 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5048 return Top::Throw(*reference_error);
5049 }
5050
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005051 if (context->IsGlobalContext()) {
5052 // 'eval' is bound in the global context, but it may have been overwritten.
5053 // Compare it to the builtin 'GlobalEval' function to make sure.
5054 Handle<JSFunction> global_eval =
5055 GetBuiltinFunction(Heap::global_eval_symbol());
5056 if (global_eval.is_identical_to(callee)) {
5057 // A direct eval call.
5058 if (args[1]->IsString()) {
5059 CONVERT_ARG_CHECKED(String, source, 1);
5060 // A normal eval call on a string. Compile it and return the
5061 // compiled function bound in the local context.
5062 Object* compiled_source = CompileDirectEval(source);
5063 if (compiled_source->IsFailure()) return compiled_source;
5064 receiver = Handle<Object>(frame->receiver());
5065 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5066 } else {
5067 // An eval call that is not called on a string. Global eval
5068 // deals better with this.
5069 receiver = Handle<Object>(Top::global_context()->global());
5070 }
5071 } else {
5072 // 'eval' is overwritten. Just call the function with the given arguments.
5073 receiver = Handle<Object>(Top::global_context()->global());
5074 }
5075 } else {
5076 // 'eval' is not bound in the global context. Just call the function
5077 // with the given arguments. This is not necessarily the global eval.
5078 if (receiver->IsContext()) {
5079 context = Handle<Context>::cast(receiver);
5080 receiver = Handle<Object>(context->get(index));
5081 }
5082 }
5083
5084 Handle<FixedArray> call = Factory::NewFixedArray(2);
5085 call->set(0, *callee);
5086 call->set(1, *receiver);
5087 return *call;
5088}
5089
5090
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005091static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5092 // This utility adjusts the property attributes for newly created Function
5093 // object ("new Function(...)") by changing the map.
5094 // All it does is changing the prototype property to enumerable
5095 // as specified in ECMA262, 15.3.5.2.
5096 HandleScope scope;
5097 ASSERT(args.length() == 1);
5098 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5099 ASSERT(func->map()->instance_type() ==
5100 Top::function_instance_map()->instance_type());
5101 ASSERT(func->map()->instance_size() ==
5102 Top::function_instance_map()->instance_size());
5103 func->set_map(*Top::function_instance_map());
5104 return *func;
5105}
5106
5107
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005108// Push an array unto an array of arrays if it is not already in the
5109// array. Returns true if the element was pushed on the stack and
5110// false otherwise.
5111static Object* Runtime_PushIfAbsent(Arguments args) {
5112 ASSERT(args.length() == 2);
5113 CONVERT_CHECKED(JSArray, array, args[0]);
5114 CONVERT_CHECKED(JSArray, element, args[1]);
5115 RUNTIME_ASSERT(array->HasFastElements());
5116 int length = Smi::cast(array->length())->value();
5117 FixedArray* elements = FixedArray::cast(array->elements());
5118 for (int i = 0; i < length; i++) {
5119 if (elements->get(i) == element) return Heap::false_value();
5120 }
5121 Object* obj = array->SetFastElement(length, element);
5122 if (obj->IsFailure()) return obj;
5123 return Heap::true_value();
5124}
5125
5126
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005127/**
5128 * A simple visitor visits every element of Array's.
5129 * The backend storage can be a fixed array for fast elements case,
5130 * or a dictionary for sparse array. Since Dictionary is a subtype
5131 * of FixedArray, the class can be used by both fast and slow cases.
5132 * The second parameter of the constructor, fast_elements, specifies
5133 * whether the storage is a FixedArray or Dictionary.
5134 *
5135 * An index limit is used to deal with the situation that a result array
5136 * length overflows 32-bit non-negative integer.
5137 */
5138class ArrayConcatVisitor {
5139 public:
5140 ArrayConcatVisitor(Handle<FixedArray> storage,
5141 uint32_t index_limit,
5142 bool fast_elements) :
5143 storage_(storage), index_limit_(index_limit),
5144 fast_elements_(fast_elements), index_offset_(0) { }
5145
5146 void visit(uint32_t i, Handle<Object> elm) {
5147 uint32_t index = i + index_offset_;
5148 if (index >= index_limit_) return;
5149
5150 if (fast_elements_) {
5151 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5152 storage_->set(index, *elm);
5153
5154 } else {
5155 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5156 Handle<Dictionary> result =
5157 Factory::DictionaryAtNumberPut(dict, index, elm);
5158 if (!result.is_identical_to(dict))
5159 storage_ = result;
5160 }
5161 }
5162
5163 void increase_index_offset(uint32_t delta) {
5164 index_offset_ += delta;
5165 }
5166
5167 private:
5168 Handle<FixedArray> storage_;
5169 uint32_t index_limit_;
5170 bool fast_elements_;
5171 uint32_t index_offset_;
5172};
5173
5174
5175/**
5176 * A helper function that visits elements of a JSObject. Only elements
5177 * whose index between 0 and range (exclusive) are visited.
5178 *
5179 * If the third parameter, visitor, is not NULL, the visitor is called
5180 * with parameters, 'visitor_index_offset + element index' and the element.
5181 *
5182 * It returns the number of visisted elements.
5183 */
5184static uint32_t IterateElements(Handle<JSObject> receiver,
5185 uint32_t range,
5186 ArrayConcatVisitor* visitor) {
5187 uint32_t num_of_elements = 0;
5188
5189 if (receiver->HasFastElements()) {
5190 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5191 uint32_t len = elements->length();
5192 if (range < len) len = range;
5193
5194 for (uint32_t j = 0; j < len; j++) {
5195 Handle<Object> e(elements->get(j));
5196 if (!e->IsTheHole()) {
5197 num_of_elements++;
5198 if (visitor)
5199 visitor->visit(j, e);
5200 }
5201 }
5202
5203 } else {
5204 Handle<Dictionary> dict(receiver->element_dictionary());
5205 uint32_t capacity = dict->Capacity();
5206 for (uint32_t j = 0; j < capacity; j++) {
5207 Handle<Object> k(dict->KeyAt(j));
5208 if (dict->IsKey(*k)) {
5209 ASSERT(k->IsNumber());
5210 uint32_t index = static_cast<uint32_t>(k->Number());
5211 if (index < range) {
5212 num_of_elements++;
5213 if (visitor) {
5214 visitor->visit(index,
5215 Handle<Object>(dict->ValueAt(j)));
5216 }
5217 }
5218 }
5219 }
5220 }
5221
5222 return num_of_elements;
5223}
5224
5225
5226/**
5227 * A helper function that visits elements of an Array object, and elements
5228 * on its prototypes.
5229 *
5230 * Elements on prototypes are visited first, and only elements whose indices
5231 * less than Array length are visited.
5232 *
5233 * If a ArrayConcatVisitor object is given, the visitor is called with
5234 * parameters, element's index + visitor_index_offset and the element.
5235 */
5236static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5237 ArrayConcatVisitor* visitor) {
5238 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5239 Handle<Object> obj = array;
5240
5241 static const int kEstimatedPrototypes = 3;
5242 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5243
5244 // Visit prototype first. If an element on the prototype is shadowed by
5245 // the inheritor using the same index, the ArrayConcatVisitor visits
5246 // the prototype element before the shadowing element.
5247 // The visitor can simply overwrite the old value by new value using
5248 // the same index. This follows Array::concat semantics.
5249 while (!obj->IsNull()) {
5250 objects.Add(Handle<JSObject>::cast(obj));
5251 obj = Handle<Object>(obj->GetPrototype());
5252 }
5253
5254 uint32_t nof_elements = 0;
5255 for (int i = objects.length() - 1; i >= 0; i--) {
5256 Handle<JSObject> obj = objects[i];
5257 nof_elements +=
5258 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5259 }
5260
5261 return nof_elements;
5262}
5263
5264
5265/**
5266 * A helper function of Runtime_ArrayConcat.
5267 *
5268 * The first argument is an Array of arrays and objects. It is the
5269 * same as the arguments array of Array::concat JS function.
5270 *
5271 * If an argument is an Array object, the function visits array
5272 * elements. If an argument is not an Array object, the function
5273 * visits the object as if it is an one-element array.
5274 *
5275 * If the result array index overflows 32-bit integer, the rounded
5276 * non-negative number is used as new length. For example, if one
5277 * array length is 2^32 - 1, second array length is 1, the
5278 * concatenated array length is 0.
5279 */
5280static uint32_t IterateArguments(Handle<JSArray> arguments,
5281 ArrayConcatVisitor* visitor) {
5282 uint32_t visited_elements = 0;
5283 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5284
5285 for (uint32_t i = 0; i < num_of_args; i++) {
5286 Handle<Object> obj(arguments->GetElement(i));
5287 if (obj->IsJSArray()) {
5288 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5289 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5290 uint32_t nof_elements =
5291 IterateArrayAndPrototypeElements(array, visitor);
5292 // Total elements of array and its prototype chain can be more than
5293 // the array length, but ArrayConcat can only concatenate at most
5294 // the array length number of elements.
5295 visited_elements += (nof_elements > len) ? len : nof_elements;
5296 if (visitor) visitor->increase_index_offset(len);
5297
5298 } else {
5299 if (visitor) {
5300 visitor->visit(0, obj);
5301 visitor->increase_index_offset(1);
5302 }
5303 visited_elements++;
5304 }
5305 }
5306 return visited_elements;
5307}
5308
5309
5310/**
5311 * Array::concat implementation.
5312 * See ECMAScript 262, 15.4.4.4.
5313 */
5314static Object* Runtime_ArrayConcat(Arguments args) {
5315 ASSERT(args.length() == 1);
5316 HandleScope handle_scope;
5317
5318 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5319 Handle<JSArray> arguments(arg_arrays);
5320
5321 // Pass 1: estimate the number of elements of the result
5322 // (it could be more than real numbers if prototype has elements).
5323 uint32_t result_length = 0;
5324 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5325
5326 { AssertNoAllocation nogc;
5327 for (uint32_t i = 0; i < num_of_args; i++) {
5328 Object* obj = arguments->GetElement(i);
5329 if (obj->IsJSArray()) {
5330 result_length +=
5331 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5332 } else {
5333 result_length++;
5334 }
5335 }
5336 }
5337
5338 // Allocate an empty array, will set length and content later.
5339 Handle<JSArray> result = Factory::NewJSArray(0);
5340
5341 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5342 // If estimated number of elements is more than half of length, a
5343 // fixed array (fast case) is more time and space-efficient than a
5344 // dictionary.
5345 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5346
5347 Handle<FixedArray> storage;
5348 if (fast_case) {
5349 // The backing storage array must have non-existing elements to
5350 // preserve holes across concat operations.
5351 storage = Factory::NewFixedArrayWithHoles(result_length);
5352
5353 } else {
5354 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5355 uint32_t at_least_space_for = estimate_nof_elements +
5356 (estimate_nof_elements >> 2);
5357 storage = Handle<FixedArray>::cast(
5358 Factory::NewDictionary(at_least_space_for));
5359 }
5360
5361 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5362
5363 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5364
5365 IterateArguments(arguments, &visitor);
5366
5367 result->set_length(*len);
5368 result->set_elements(*storage);
5369
5370 return *result;
5371}
5372
5373
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005374// This will not allocate (flatten the string), but it may run
5375// very slowly for very deeply nested ConsStrings. For debugging use only.
5376static Object* Runtime_GlobalPrint(Arguments args) {
5377 NoHandleAllocation ha;
5378 ASSERT(args.length() == 1);
5379
5380 CONVERT_CHECKED(String, string, args[0]);
5381 StringInputBuffer buffer(string);
5382 while (buffer.has_more()) {
5383 uint16_t character = buffer.GetNext();
5384 PrintF("%c", character);
5385 }
5386 return string;
5387}
5388
ager@chromium.org5ec48922009-05-05 07:25:34 +00005389// Moves all own elements of an object, that are below a limit, to positions
5390// starting at zero. All undefined values are placed after non-undefined values,
5391// and are followed by non-existing element. Does not change the length
5392// property.
5393// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005394static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005395 ASSERT(args.length() == 2);
5396 CONVERT_CHECKED(JSObject, object, args[0]);
5397 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5398 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005399}
5400
5401
5402// Move contents of argument 0 (an array) to argument 1 (an array)
5403static Object* Runtime_MoveArrayContents(Arguments args) {
5404 ASSERT(args.length() == 2);
5405 CONVERT_CHECKED(JSArray, from, args[0]);
5406 CONVERT_CHECKED(JSArray, to, args[1]);
5407 to->SetContent(FixedArray::cast(from->elements()));
5408 to->set_length(from->length());
5409 from->SetContent(Heap::empty_fixed_array());
5410 from->set_length(0);
5411 return to;
5412}
5413
5414
5415// How many elements does this array have?
5416static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5417 ASSERT(args.length() == 1);
5418 CONVERT_CHECKED(JSArray, array, args[0]);
5419 HeapObject* elements = array->elements();
5420 if (elements->IsDictionary()) {
5421 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5422 } else {
5423 return array->length();
5424 }
5425}
5426
5427
5428// Returns an array that tells you where in the [0, length) interval an array
5429// might have elements. Can either return keys or intervals. Keys can have
5430// gaps in (undefined). Intervals can also span over some undefined keys.
5431static Object* Runtime_GetArrayKeys(Arguments args) {
5432 ASSERT(args.length() == 2);
5433 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005434 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005435 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005436 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005437 // Create an array and get all the keys into it, then remove all the
5438 // keys that are not integers in the range 0 to length-1.
5439 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5440 int keys_length = keys->length();
5441 for (int i = 0; i < keys_length; i++) {
5442 Object* key = keys->get(i);
5443 uint32_t index;
5444 if (!Array::IndexFromObject(key, &index) || index >= length) {
5445 // Zap invalid keys.
5446 keys->set_undefined(i);
5447 }
5448 }
5449 return *Factory::NewJSArrayWithElements(keys);
5450 } else {
5451 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5452 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005453 single_interval->set(0,
5454 Smi::FromInt(-1),
5455 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005456 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5457 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005458 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005459 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005460 single_interval->set(1, *length_object);
5461 return *Factory::NewJSArrayWithElements(single_interval);
5462 }
5463}
5464
5465
5466// DefineAccessor takes an optional final argument which is the
5467// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5468// to the way accessors are implemented, it is set for both the getter
5469// and setter on the first call to DefineAccessor and ignored on
5470// subsequent calls.
5471static Object* Runtime_DefineAccessor(Arguments args) {
5472 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5473 // Compute attributes.
5474 PropertyAttributes attributes = NONE;
5475 if (args.length() == 5) {
5476 CONVERT_CHECKED(Smi, attrs, args[4]);
5477 int value = attrs->value();
5478 // Only attribute bits should be set.
5479 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5480 attributes = static_cast<PropertyAttributes>(value);
5481 }
5482
5483 CONVERT_CHECKED(JSObject, obj, args[0]);
5484 CONVERT_CHECKED(String, name, args[1]);
5485 CONVERT_CHECKED(Smi, flag, args[2]);
5486 CONVERT_CHECKED(JSFunction, fun, args[3]);
5487 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5488}
5489
5490
5491static Object* Runtime_LookupAccessor(Arguments args) {
5492 ASSERT(args.length() == 3);
5493 CONVERT_CHECKED(JSObject, obj, args[0]);
5494 CONVERT_CHECKED(String, name, args[1]);
5495 CONVERT_CHECKED(Smi, flag, args[2]);
5496 return obj->LookupAccessor(name, flag->value() == 0);
5497}
5498
5499
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005500#ifdef ENABLE_DEBUGGER_SUPPORT
5501static Object* Runtime_DebugBreak(Arguments args) {
5502 ASSERT(args.length() == 0);
5503 return Execution::DebugBreakHelper();
5504}
5505
5506
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005507// Helper functions for wrapping and unwrapping stack frame ids.
5508static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005509 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005510 return Smi::FromInt(id >> 2);
5511}
5512
5513
5514static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5515 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5516}
5517
5518
5519// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005520// args[0]: debug event listener function to set or null or undefined for
5521// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005522// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005523static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005524 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005525 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5526 args[0]->IsUndefined() ||
5527 args[0]->IsNull());
5528 Handle<Object> callback = args.at<Object>(0);
5529 Handle<Object> data = args.at<Object>(1);
5530 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005531
5532 return Heap::undefined_value();
5533}
5534
5535
5536static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005537 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005538 StackGuard::DebugBreak();
5539 return Heap::undefined_value();
5540}
5541
5542
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005543// Find the length of the prototype chain that is to to handled as one. If a
5544// prototype object is hidden it is to be viewed as part of the the object it
5545// is prototype for.
5546static int LocalPrototypeChainLength(JSObject* obj) {
5547 int count = 1;
5548 Object* proto = obj->GetPrototype();
5549 while (proto->IsJSObject() &&
5550 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5551 count++;
5552 proto = JSObject::cast(proto)->GetPrototype();
5553 }
5554 return count;
5555}
5556
5557
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005558static Object* DebugLookupResultValue(Object* receiver, String* name,
5559 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005560 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005561 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005562 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005563 case NORMAL: {
5564 Dictionary* dict =
5565 JSObject::cast(result->holder())->property_dictionary();
5566 value = dict->ValueAt(result->GetDictionaryEntry());
5567 if (value->IsTheHole()) {
5568 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005569 }
5570 return value;
5571 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005572 case FIELD:
5573 value =
5574 JSObject::cast(
5575 result->holder())->FastPropertyAt(result->GetFieldIndex());
5576 if (value->IsTheHole()) {
5577 return Heap::undefined_value();
5578 }
5579 return value;
5580 case CONSTANT_FUNCTION:
5581 return result->GetConstantFunction();
5582 case CALLBACKS: {
5583 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005584 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005585 value = receiver->GetPropertyWithCallback(
5586 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005587 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005588 value = Top::pending_exception();
5589 Top::clear_pending_exception();
5590 if (caught_exception != NULL) {
5591 *caught_exception = true;
5592 }
5593 }
5594 return value;
5595 } else {
5596 return Heap::undefined_value();
5597 }
5598 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005599 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005600 case MAP_TRANSITION:
5601 case CONSTANT_TRANSITION:
5602 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005603 return Heap::undefined_value();
5604 default:
5605 UNREACHABLE();
5606 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005607 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005608 return Heap::undefined_value();
5609}
5610
5611
ager@chromium.org32912102009-01-16 10:38:43 +00005612// Get debugger related details for an object property.
5613// args[0]: object holding property
5614// args[1]: name of the property
5615//
5616// The array returned contains the following information:
5617// 0: Property value
5618// 1: Property details
5619// 2: Property value is exception
5620// 3: Getter function if defined
5621// 4: Setter function if defined
5622// Items 2-4 are only filled if the property has either a getter or a setter
5623// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005624static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005625 HandleScope scope;
5626
5627 ASSERT(args.length() == 2);
5628
5629 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5630 CONVERT_ARG_CHECKED(String, name, 1);
5631
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005632 // Make sure to set the current context to the context before the debugger was
5633 // entered (if the debugger is entered). The reason for switching context here
5634 // is that for some property lookups (accessors and interceptors) callbacks
5635 // into the embedding application can occour, and the embedding application
5636 // could have the assumption that its own global context is the current
5637 // context and not some internal debugger context.
5638 SaveContext save;
5639 if (Debug::InDebugger()) {
5640 Top::set_context(*Debug::debugger_entry()->GetContext());
5641 }
5642
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005643 // Skip the global proxy as it has no properties and always delegates to the
5644 // real global object.
5645 if (obj->IsJSGlobalProxy()) {
5646 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5647 }
5648
5649
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005650 // Check if the name is trivially convertible to an index and get the element
5651 // if so.
5652 uint32_t index;
5653 if (name->AsArrayIndex(&index)) {
5654 Handle<FixedArray> details = Factory::NewFixedArray(2);
5655 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5656 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5657 return *Factory::NewJSArrayWithElements(details);
5658 }
5659
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005660 // Find the number of objects making up this.
5661 int length = LocalPrototypeChainLength(*obj);
5662
5663 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005665 Handle<JSObject> jsproto = obj;
5666 for (int i = 0; i < length; i++) {
5667 jsproto->LocalLookup(*name, &result);
5668 if (result.IsProperty()) {
5669 break;
5670 }
5671 if (i < length - 1) {
5672 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5673 }
5674 }
5675
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005676 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005677 // LookupResult is not GC safe as all its members are raw object pointers.
5678 // When calling DebugLookupResultValue GC can happen as this might invoke
5679 // callbacks. After the call to DebugLookupResultValue the callback object
5680 // in the LookupResult might still be needed. Put it into a handle for later
5681 // use.
5682 PropertyType result_type = result.type();
5683 Handle<Object> result_callback_obj;
5684 if (result_type == CALLBACKS) {
5685 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5686 }
5687
5688 // Find the actual value. Don't use result after this call as it's content
5689 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005690 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005691 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005692 &caught_exception);
5693 if (value->IsFailure()) return value;
5694 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005695
ager@chromium.org32912102009-01-16 10:38:43 +00005696 // If the callback object is a fixed array then it contains JavaScript
5697 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005698 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5699 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005700 Handle<FixedArray> details =
5701 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005702 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005703 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005704 if (hasJavaScriptAccessors) {
5705 details->set(2,
5706 caught_exception ? Heap::true_value() : Heap::false_value());
5707 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5708 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5709 }
5710
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005711 return *Factory::NewJSArrayWithElements(details);
5712 }
5713 return Heap::undefined_value();
5714}
5715
5716
5717static Object* Runtime_DebugGetProperty(Arguments args) {
5718 HandleScope scope;
5719
5720 ASSERT(args.length() == 2);
5721
5722 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5723 CONVERT_ARG_CHECKED(String, name, 1);
5724
5725 LookupResult result;
5726 obj->Lookup(*name, &result);
5727 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005728 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005729 }
5730 return Heap::undefined_value();
5731}
5732
5733
5734// Return the names of the local named properties.
5735// args[0]: object
5736static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5737 HandleScope scope;
5738 ASSERT(args.length() == 1);
5739 if (!args[0]->IsJSObject()) {
5740 return Heap::undefined_value();
5741 }
5742 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5743
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005744 // Skip the global proxy as it has no properties and always delegates to the
5745 // real global object.
5746 if (obj->IsJSGlobalProxy()) {
5747 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5748 }
5749
5750 // Find the number of objects making up this.
5751 int length = LocalPrototypeChainLength(*obj);
5752
5753 // Find the number of local properties for each of the objects.
5754 int* local_property_count = NewArray<int>(length);
5755 int total_property_count = 0;
5756 Handle<JSObject> jsproto = obj;
5757 for (int i = 0; i < length; i++) {
5758 int n;
5759 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5760 local_property_count[i] = n;
5761 total_property_count += n;
5762 if (i < length - 1) {
5763 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5764 }
5765 }
5766
5767 // Allocate an array with storage for all the property names.
5768 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5769
5770 // Get the property names.
5771 jsproto = obj;
5772 for (int i = 0; i < length; i++) {
5773 jsproto->GetLocalPropertyNames(*names,
5774 i == 0 ? 0 : local_property_count[i - 1]);
5775 if (i < length - 1) {
5776 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5777 }
5778 }
5779
5780 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005781 return *Factory::NewJSArrayWithElements(names);
5782}
5783
5784
5785// Return the names of the local indexed properties.
5786// args[0]: object
5787static Object* Runtime_DebugLocalElementNames(Arguments args) {
5788 HandleScope scope;
5789 ASSERT(args.length() == 1);
5790 if (!args[0]->IsJSObject()) {
5791 return Heap::undefined_value();
5792 }
5793 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5794
5795 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5796 Handle<FixedArray> names = Factory::NewFixedArray(n);
5797 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5798 return *Factory::NewJSArrayWithElements(names);
5799}
5800
5801
5802// Return the property type calculated from the property details.
5803// args[0]: smi with property details.
5804static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5805 ASSERT(args.length() == 1);
5806 CONVERT_CHECKED(Smi, details, args[0]);
5807 PropertyType type = PropertyDetails(details).type();
5808 return Smi::FromInt(static_cast<int>(type));
5809}
5810
5811
5812// Return the property attribute calculated from the property details.
5813// args[0]: smi with property details.
5814static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5815 ASSERT(args.length() == 1);
5816 CONVERT_CHECKED(Smi, details, args[0]);
5817 PropertyAttributes attributes = PropertyDetails(details).attributes();
5818 return Smi::FromInt(static_cast<int>(attributes));
5819}
5820
5821
5822// Return the property insertion index calculated from the property details.
5823// args[0]: smi with property details.
5824static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5825 ASSERT(args.length() == 1);
5826 CONVERT_CHECKED(Smi, details, args[0]);
5827 int index = PropertyDetails(details).index();
5828 return Smi::FromInt(index);
5829}
5830
5831
5832// Return information on whether an object has a named or indexed interceptor.
5833// args[0]: object
5834static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5835 HandleScope scope;
5836 ASSERT(args.length() == 1);
5837 if (!args[0]->IsJSObject()) {
5838 return Smi::FromInt(0);
5839 }
5840 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5841
5842 int result = 0;
5843 if (obj->HasNamedInterceptor()) result |= 2;
5844 if (obj->HasIndexedInterceptor()) result |= 1;
5845
5846 return Smi::FromInt(result);
5847}
5848
5849
5850// Return property names from named interceptor.
5851// args[0]: object
5852static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5853 HandleScope scope;
5854 ASSERT(args.length() == 1);
5855 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005856
ager@chromium.org32912102009-01-16 10:38:43 +00005857 if (obj->HasNamedInterceptor()) {
5858 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5859 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5860 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005861 return Heap::undefined_value();
5862}
5863
5864
5865// Return element names from indexed interceptor.
5866// args[0]: object
5867static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5868 HandleScope scope;
5869 ASSERT(args.length() == 1);
5870 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005871
ager@chromium.org32912102009-01-16 10:38:43 +00005872 if (obj->HasIndexedInterceptor()) {
5873 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5874 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5875 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005876 return Heap::undefined_value();
5877}
5878
5879
5880// Return property value from named interceptor.
5881// args[0]: object
5882// args[1]: property name
5883static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5884 HandleScope scope;
5885 ASSERT(args.length() == 2);
5886 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5887 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5888 CONVERT_ARG_CHECKED(String, name, 1);
5889
5890 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005891 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005892}
5893
5894
5895// Return element value from indexed interceptor.
5896// args[0]: object
5897// args[1]: index
5898static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5899 HandleScope scope;
5900 ASSERT(args.length() == 2);
5901 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5902 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5903 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5904
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005905 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005906}
5907
5908
5909static Object* Runtime_CheckExecutionState(Arguments args) {
5910 ASSERT(args.length() >= 1);
5911 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005912 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005913 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005914 return Top::Throw(Heap::illegal_execution_state_symbol());
5915 }
5916
5917 return Heap::true_value();
5918}
5919
5920
5921static Object* Runtime_GetFrameCount(Arguments args) {
5922 HandleScope scope;
5923 ASSERT(args.length() == 1);
5924
5925 // Check arguments.
5926 Object* result = Runtime_CheckExecutionState(args);
5927 if (result->IsFailure()) return result;
5928
5929 // Count all frames which are relevant to debugging stack trace.
5930 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005931 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005932 if (id == StackFrame::NO_ID) {
5933 // If there is no JavaScript stack frame count is 0.
5934 return Smi::FromInt(0);
5935 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005936 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5937 return Smi::FromInt(n);
5938}
5939
5940
5941static const int kFrameDetailsFrameIdIndex = 0;
5942static const int kFrameDetailsReceiverIndex = 1;
5943static const int kFrameDetailsFunctionIndex = 2;
5944static const int kFrameDetailsArgumentCountIndex = 3;
5945static const int kFrameDetailsLocalCountIndex = 4;
5946static const int kFrameDetailsSourcePositionIndex = 5;
5947static const int kFrameDetailsConstructCallIndex = 6;
5948static const int kFrameDetailsDebuggerFrameIndex = 7;
5949static const int kFrameDetailsFirstDynamicIndex = 8;
5950
5951// Return an array with frame details
5952// args[0]: number: break id
5953// args[1]: number: frame index
5954//
5955// The array returned contains the following information:
5956// 0: Frame id
5957// 1: Receiver
5958// 2: Function
5959// 3: Argument count
5960// 4: Local count
5961// 5: Source position
5962// 6: Constructor call
5963// 7: Debugger frame
5964// Arguments name, value
5965// Locals name, value
5966static Object* Runtime_GetFrameDetails(Arguments args) {
5967 HandleScope scope;
5968 ASSERT(args.length() == 2);
5969
5970 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005971 Object* check = Runtime_CheckExecutionState(args);
5972 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005973 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5974
5975 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005976 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005977 if (id == StackFrame::NO_ID) {
5978 // If there are no JavaScript stack frames return undefined.
5979 return Heap::undefined_value();
5980 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005981 int count = 0;
5982 JavaScriptFrameIterator it(id);
5983 for (; !it.done(); it.Advance()) {
5984 if (count == index) break;
5985 count++;
5986 }
5987 if (it.done()) return Heap::undefined_value();
5988
5989 // Traverse the saved contexts chain to find the active context for the
5990 // selected frame.
5991 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005992 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005993 save = save->prev();
5994 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005995 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996
5997 // Get the frame id.
5998 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5999
6000 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006001 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006002
6003 // Check for constructor frame.
6004 bool constructor = it.frame()->IsConstructor();
6005
6006 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006007 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006008 ScopeInfo<> info(*code);
6009
6010 // Get the context.
6011 Handle<Context> context(Context::cast(it.frame()->context()));
6012
6013 // Get the locals names and values into a temporary array.
6014 //
6015 // TODO(1240907): Hide compiler-introduced stack variables
6016 // (e.g. .result)? For users of the debugger, they will probably be
6017 // confusing.
6018 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6019 for (int i = 0; i < info.NumberOfLocals(); i++) {
6020 // Name of the local.
6021 locals->set(i * 2, *info.LocalName(i));
6022
6023 // Fetch the value of the local - either from the stack or from a
6024 // heap-allocated context.
6025 if (i < info.number_of_stack_slots()) {
6026 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6027 } else {
6028 Handle<String> name = info.LocalName(i);
6029 // Traverse the context chain to the function context as all local
6030 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006031 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006032 context = Handle<Context>(context->previous());
6033 }
6034 ASSERT(context->is_function_context());
6035 locals->set(i * 2 + 1,
6036 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6037 NULL)));
6038 }
6039 }
6040
6041 // Now advance to the arguments adapter frame (if any). If contains all
6042 // the provided parameters and
6043
6044 // Now advance to the arguments adapter frame (if any). It contains all
6045 // the provided parameters whereas the function frame always have the number
6046 // of arguments matching the functions parameters. The rest of the
6047 // information (except for what is collected above) is the same.
6048 it.AdvanceToArgumentsFrame();
6049
6050 // Find the number of arguments to fill. At least fill the number of
6051 // parameters for the function and fill more if more parameters are provided.
6052 int argument_count = info.number_of_parameters();
6053 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6054 argument_count = it.frame()->GetProvidedParametersCount();
6055 }
6056
6057 // Calculate the size of the result.
6058 int details_size = kFrameDetailsFirstDynamicIndex +
6059 2 * (argument_count + info.NumberOfLocals());
6060 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6061
6062 // Add the frame id.
6063 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6064
6065 // Add the function (same as in function frame).
6066 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6067
6068 // Add the arguments count.
6069 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6070
6071 // Add the locals count
6072 details->set(kFrameDetailsLocalCountIndex,
6073 Smi::FromInt(info.NumberOfLocals()));
6074
6075 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006076 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006077 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6078 } else {
6079 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6080 }
6081
6082 // Add the constructor information.
6083 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6084
6085 // Add information on whether this frame is invoked in the debugger context.
6086 details->set(kFrameDetailsDebuggerFrameIndex,
6087 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6088
6089 // Fill the dynamic part.
6090 int details_index = kFrameDetailsFirstDynamicIndex;
6091
6092 // Add arguments name and value.
6093 for (int i = 0; i < argument_count; i++) {
6094 // Name of the argument.
6095 if (i < info.number_of_parameters()) {
6096 details->set(details_index++, *info.parameter_name(i));
6097 } else {
6098 details->set(details_index++, Heap::undefined_value());
6099 }
6100
6101 // Parameter value.
6102 if (i < it.frame()->GetProvidedParametersCount()) {
6103 details->set(details_index++, it.frame()->GetParameter(i));
6104 } else {
6105 details->set(details_index++, Heap::undefined_value());
6106 }
6107 }
6108
6109 // Add locals name and value from the temporary copy from the function frame.
6110 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6111 details->set(details_index++, locals->get(i));
6112 }
6113
6114 // Add the receiver (same as in function frame).
6115 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6116 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6117 Handle<Object> receiver(it.frame()->receiver());
6118 if (!receiver->IsJSObject()) {
6119 // If the receiver is NOT a JSObject we have hit an optimization
6120 // where a value object is not converted into a wrapped JS objects.
6121 // To hide this optimization from the debugger, we wrap the receiver
6122 // by creating correct wrapper object based on the calling frame's
6123 // global context.
6124 it.Advance();
6125 Handle<Context> calling_frames_global_context(
6126 Context::cast(Context::cast(it.frame()->context())->global_context()));
6127 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6128 }
6129 details->set(kFrameDetailsReceiverIndex, *receiver);
6130
6131 ASSERT_EQ(details_size, details_index);
6132 return *Factory::NewJSArrayWithElements(details);
6133}
6134
6135
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006136// Copy all the context locals into an object used to materialize a scope.
6137static void CopyContextLocalsToScopeObject(Handle<Code> code,
6138 ScopeInfo<>& scope_info,
6139 Handle<Context> context,
6140 Handle<JSObject> scope_object) {
6141 // Fill all context locals to the context extension.
6142 for (int i = Context::MIN_CONTEXT_SLOTS;
6143 i < scope_info.number_of_context_slots();
6144 i++) {
6145 int context_index =
6146 ScopeInfo<>::ContextSlotIndex(*code,
6147 *scope_info.context_slot_name(i),
6148 NULL);
6149
6150 // Don't include the arguments shadow (.arguments) context variable.
6151 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6152 SetProperty(scope_object,
6153 scope_info.context_slot_name(i),
6154 Handle<Object>(context->get(context_index)), NONE);
6155 }
6156 }
6157}
6158
6159
6160// Create a plain JSObject which materializes the local scope for the specified
6161// frame.
6162static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6163 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6164 Handle<Code> code(function->code());
6165 ScopeInfo<> scope_info(*code);
6166
6167 // Allocate and initialize a JSObject with all the arguments, stack locals
6168 // heap locals and extension properties of the debugged function.
6169 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6170
6171 // First fill all parameters.
6172 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6173 SetProperty(local_scope,
6174 scope_info.parameter_name(i),
6175 Handle<Object>(frame->GetParameter(i)), NONE);
6176 }
6177
6178 // Second fill all stack locals.
6179 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6180 SetProperty(local_scope,
6181 scope_info.stack_slot_name(i),
6182 Handle<Object>(frame->GetExpression(i)), NONE);
6183 }
6184
6185 // Third fill all context locals.
6186 Handle<Context> frame_context(Context::cast(frame->context()));
6187 Handle<Context> function_context(frame_context->fcontext());
6188 CopyContextLocalsToScopeObject(code, scope_info,
6189 function_context, local_scope);
6190
6191 // Finally copy any properties from the function context extension. This will
6192 // be variables introduced by eval.
6193 if (function_context->closure() == *function) {
6194 if (function_context->has_extension() &&
6195 !function_context->IsGlobalContext()) {
6196 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6197 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6198 for (int i = 0; i < keys->length(); i++) {
6199 // Names of variables introduced by eval are strings.
6200 ASSERT(keys->get(i)->IsString());
6201 Handle<String> key(String::cast(keys->get(i)));
6202 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6203 }
6204 }
6205 }
6206 return local_scope;
6207}
6208
6209
6210// Create a plain JSObject which materializes the closure content for the
6211// context.
6212static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6213 ASSERT(context->is_function_context());
6214
6215 Handle<Code> code(context->closure()->code());
6216 ScopeInfo<> scope_info(*code);
6217
6218 // Allocate and initialize a JSObject with all the content of theis function
6219 // closure.
6220 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6221
6222 // Check whether the arguments shadow object exists.
6223 int arguments_shadow_index =
6224 ScopeInfo<>::ContextSlotIndex(*code,
6225 Heap::arguments_shadow_symbol(),
6226 NULL);
6227 if (arguments_shadow_index >= 0) {
6228 // In this case all the arguments are available in the arguments shadow
6229 // object.
6230 Handle<JSObject> arguments_shadow(
6231 JSObject::cast(context->get(arguments_shadow_index)));
6232 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6233 SetProperty(closure_scope,
6234 scope_info.parameter_name(i),
6235 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6236 }
6237 }
6238
6239 // Fill all context locals to the context extension.
6240 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6241
6242 // Finally copy any properties from the function context extension. This will
6243 // be variables introduced by eval.
6244 if (context->has_extension()) {
6245 Handle<JSObject> ext(JSObject::cast(context->extension()));
6246 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6247 for (int i = 0; i < keys->length(); i++) {
6248 // Names of variables introduced by eval are strings.
6249 ASSERT(keys->get(i)->IsString());
6250 Handle<String> key(String::cast(keys->get(i)));
6251 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6252 }
6253 }
6254
6255 return closure_scope;
6256}
6257
6258
6259// Iterate over the actual scopes visible from a stack frame. All scopes are
6260// backed by an actual context except the local scope, which is inserted
6261// "artifically" in the context chain.
6262class ScopeIterator {
6263 public:
6264 enum ScopeType {
6265 ScopeTypeGlobal = 0,
6266 ScopeTypeLocal,
6267 ScopeTypeWith,
6268 ScopeTypeClosure
6269 };
6270
6271 explicit ScopeIterator(JavaScriptFrame* frame)
6272 : frame_(frame),
6273 function_(JSFunction::cast(frame->function())),
6274 context_(Context::cast(frame->context())),
6275 local_done_(false),
6276 at_local_(false) {
6277
6278 // Check whether the first scope is actually a local scope.
6279 if (context_->IsGlobalContext()) {
6280 // If there is a stack slot for .result then this local scope has been
6281 // created for evaluating top level code and it is not a real local scope.
6282 // Checking for the existence of .result seems fragile, but the scope info
6283 // saved with the code object does not otherwise have that information.
6284 Handle<Code> code(function_->code());
6285 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6286 at_local_ = index < 0;
6287 } else if (context_->is_function_context()) {
6288 at_local_ = true;
6289 }
6290 }
6291
6292 // More scopes?
6293 bool Done() { return context_.is_null(); }
6294
6295 // Move to the next scope.
6296 void Next() {
6297 // If at a local scope mark the local scope as passed.
6298 if (at_local_) {
6299 at_local_ = false;
6300 local_done_ = true;
6301
6302 // If the current context is not associated with the local scope the
6303 // current context is the next real scope, so don't move to the next
6304 // context in this case.
6305 if (context_->closure() != *function_) {
6306 return;
6307 }
6308 }
6309
6310 // The global scope is always the last in the chain.
6311 if (context_->IsGlobalContext()) {
6312 context_ = Handle<Context>();
6313 return;
6314 }
6315
6316 // Move to the next context.
6317 if (context_->is_function_context()) {
6318 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6319 } else {
6320 context_ = Handle<Context>(context_->previous());
6321 }
6322
6323 // If passing the local scope indicate that the current scope is now the
6324 // local scope.
6325 if (!local_done_ &&
6326 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6327 at_local_ = true;
6328 }
6329 }
6330
6331 // Return the type of the current scope.
6332 int Type() {
6333 if (at_local_) {
6334 return ScopeTypeLocal;
6335 }
6336 if (context_->IsGlobalContext()) {
6337 ASSERT(context_->global()->IsGlobalObject());
6338 return ScopeTypeGlobal;
6339 }
6340 if (context_->is_function_context()) {
6341 return ScopeTypeClosure;
6342 }
6343 ASSERT(context_->has_extension());
6344 ASSERT(!context_->extension()->IsJSContextExtensionObject());
6345 return ScopeTypeWith;
6346 }
6347
6348 // Return the JavaScript object with the content of the current scope.
6349 Handle<JSObject> ScopeObject() {
6350 switch (Type()) {
6351 case ScopeIterator::ScopeTypeGlobal:
6352 return Handle<JSObject>(CurrentContext()->global());
6353 break;
6354 case ScopeIterator::ScopeTypeLocal:
6355 // Materialize the content of the local scope into a JSObject.
6356 return MaterializeLocalScope(frame_);
6357 break;
6358 case ScopeIterator::ScopeTypeWith:
6359 // Return the with object.
6360 return Handle<JSObject>(CurrentContext()->extension());
6361 break;
6362 case ScopeIterator::ScopeTypeClosure:
6363 // Materialize the content of the closure scope into a JSObject.
6364 return MaterializeClosure(CurrentContext());
6365 break;
6366 }
6367 UNREACHABLE();
6368 return Handle<JSObject>();
6369 }
6370
6371 // Return the context for this scope. For the local context there might not
6372 // be an actual context.
6373 Handle<Context> CurrentContext() {
6374 if (at_local_ && context_->closure() != *function_) {
6375 return Handle<Context>();
6376 }
6377 return context_;
6378 }
6379
6380#ifdef DEBUG
6381 // Debug print of the content of the current scope.
6382 void DebugPrint() {
6383 switch (Type()) {
6384 case ScopeIterator::ScopeTypeGlobal:
6385 PrintF("Global:\n");
6386 CurrentContext()->Print();
6387 break;
6388
6389 case ScopeIterator::ScopeTypeLocal: {
6390 PrintF("Local:\n");
6391 Handle<Code> code(function_->code());
6392 ScopeInfo<> scope_info(*code);
6393 scope_info.Print();
6394 if (!CurrentContext().is_null()) {
6395 CurrentContext()->Print();
6396 if (CurrentContext()->has_extension()) {
6397 Handle<JSObject> extension =
6398 Handle<JSObject>(CurrentContext()->extension());
6399 if (extension->IsJSContextExtensionObject()) {
6400 extension->Print();
6401 }
6402 }
6403 }
6404 break;
6405 }
6406
6407 case ScopeIterator::ScopeTypeWith: {
6408 PrintF("With:\n");
6409 Handle<JSObject> extension =
6410 Handle<JSObject>(CurrentContext()->extension());
6411 extension->Print();
6412 break;
6413 }
6414
6415 case ScopeIterator::ScopeTypeClosure: {
6416 PrintF("Closure:\n");
6417 CurrentContext()->Print();
6418 if (CurrentContext()->has_extension()) {
6419 Handle<JSObject> extension =
6420 Handle<JSObject>(CurrentContext()->extension());
6421 if (extension->IsJSContextExtensionObject()) {
6422 extension->Print();
6423 }
6424 }
6425 break;
6426 }
6427
6428 default:
6429 UNREACHABLE();
6430 }
6431 PrintF("\n");
6432 }
6433#endif
6434
6435 private:
6436 JavaScriptFrame* frame_;
6437 Handle<JSFunction> function_;
6438 Handle<Context> context_;
6439 bool local_done_;
6440 bool at_local_;
6441
6442 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6443};
6444
6445
6446static Object* Runtime_GetScopeCount(Arguments args) {
6447 HandleScope scope;
6448 ASSERT(args.length() == 2);
6449
6450 // Check arguments.
6451 Object* check = Runtime_CheckExecutionState(args);
6452 if (check->IsFailure()) return check;
6453 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6454
6455 // Get the frame where the debugging is performed.
6456 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6457 JavaScriptFrameIterator it(id);
6458 JavaScriptFrame* frame = it.frame();
6459
6460 // Count the visible scopes.
6461 int n = 0;
6462 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6463 n++;
6464 }
6465
6466 return Smi::FromInt(n);
6467}
6468
6469
6470static const int kScopeDetailsTypeIndex = 0;
6471static const int kScopeDetailsObjectIndex = 1;
6472static const int kScopeDetailsSize = 2;
6473
6474// Return an array with scope details
6475// args[0]: number: break id
6476// args[1]: number: frame index
6477// args[2]: number: scope index
6478//
6479// The array returned contains the following information:
6480// 0: Scope type
6481// 1: Scope object
6482static Object* Runtime_GetScopeDetails(Arguments args) {
6483 HandleScope scope;
6484 ASSERT(args.length() == 3);
6485
6486 // Check arguments.
6487 Object* check = Runtime_CheckExecutionState(args);
6488 if (check->IsFailure()) return check;
6489 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6490 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6491
6492 // Get the frame where the debugging is performed.
6493 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6494 JavaScriptFrameIterator frame_it(id);
6495 JavaScriptFrame* frame = frame_it.frame();
6496
6497 // Find the requested scope.
6498 int n = 0;
6499 ScopeIterator it(frame);
6500 for (; !it.Done() && n < index; it.Next()) {
6501 n++;
6502 }
6503 if (it.Done()) {
6504 return Heap::undefined_value();
6505 }
6506
6507 // Calculate the size of the result.
6508 int details_size = kScopeDetailsSize;
6509 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6510
6511 // Fill in scope details.
6512 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6513 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6514
6515 return *Factory::NewJSArrayWithElements(details);
6516}
6517
6518
6519static Object* Runtime_DebugPrintScopes(Arguments args) {
6520 HandleScope scope;
6521 ASSERT(args.length() == 0);
6522
6523#ifdef DEBUG
6524 // Print the scopes for the top frame.
6525 StackFrameLocator locator;
6526 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6527 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6528 it.DebugPrint();
6529 }
6530#endif
6531 return Heap::undefined_value();
6532}
6533
6534
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006535static Object* Runtime_GetCFrames(Arguments args) {
6536 HandleScope scope;
6537 ASSERT(args.length() == 1);
6538 Object* result = Runtime_CheckExecutionState(args);
6539 if (result->IsFailure()) return result;
6540
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006541#if V8_HOST_ARCH_64_BIT
6542 UNIMPLEMENTED();
6543 return Heap::undefined_value();
6544#else
6545
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006546 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006547 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6548 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006549 if (frames_count == OS::kStackWalkError) {
6550 return Heap::undefined_value();
6551 }
6552
6553 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6554 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6555 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6556 for (int i = 0; i < frames_count; i++) {
6557 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6558 frame_value->SetProperty(
6559 *address_str,
6560 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6561 NONE);
6562
6563 // Get the stack walk text for this frame.
6564 Handle<String> frame_text;
6565 if (strlen(frames[i].text) > 0) {
6566 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6567 frame_text = Factory::NewStringFromAscii(str);
6568 }
6569
6570 if (!frame_text.is_null()) {
6571 frame_value->SetProperty(*text_str, *frame_text, NONE);
6572 }
6573
6574 frames_array->set(i, *frame_value);
6575 }
6576 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006577#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006578}
6579
6580
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006581static Object* Runtime_GetThreadCount(Arguments args) {
6582 HandleScope scope;
6583 ASSERT(args.length() == 1);
6584
6585 // Check arguments.
6586 Object* result = Runtime_CheckExecutionState(args);
6587 if (result->IsFailure()) return result;
6588
6589 // Count all archived V8 threads.
6590 int n = 0;
6591 for (ThreadState* thread = ThreadState::FirstInUse();
6592 thread != NULL;
6593 thread = thread->Next()) {
6594 n++;
6595 }
6596
6597 // Total number of threads is current thread and archived threads.
6598 return Smi::FromInt(n + 1);
6599}
6600
6601
6602static const int kThreadDetailsCurrentThreadIndex = 0;
6603static const int kThreadDetailsThreadIdIndex = 1;
6604static const int kThreadDetailsSize = 2;
6605
6606// Return an array with thread details
6607// args[0]: number: break id
6608// args[1]: number: thread index
6609//
6610// The array returned contains the following information:
6611// 0: Is current thread?
6612// 1: Thread id
6613static Object* Runtime_GetThreadDetails(Arguments args) {
6614 HandleScope scope;
6615 ASSERT(args.length() == 2);
6616
6617 // Check arguments.
6618 Object* check = Runtime_CheckExecutionState(args);
6619 if (check->IsFailure()) return check;
6620 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6621
6622 // Allocate array for result.
6623 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6624
6625 // Thread index 0 is current thread.
6626 if (index == 0) {
6627 // Fill the details.
6628 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6629 details->set(kThreadDetailsThreadIdIndex,
6630 Smi::FromInt(ThreadManager::CurrentId()));
6631 } else {
6632 // Find the thread with the requested index.
6633 int n = 1;
6634 ThreadState* thread = ThreadState::FirstInUse();
6635 while (index != n && thread != NULL) {
6636 thread = thread->Next();
6637 n++;
6638 }
6639 if (thread == NULL) {
6640 return Heap::undefined_value();
6641 }
6642
6643 // Fill the details.
6644 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6645 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6646 }
6647
6648 // Convert to JS array and return.
6649 return *Factory::NewJSArrayWithElements(details);
6650}
6651
6652
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006653static Object* Runtime_GetBreakLocations(Arguments args) {
6654 HandleScope scope;
6655 ASSERT(args.length() == 1);
6656
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006657 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6658 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006659 // Find the number of break points
6660 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6661 if (break_locations->IsUndefined()) return Heap::undefined_value();
6662 // Return array as JS array
6663 return *Factory::NewJSArrayWithElements(
6664 Handle<FixedArray>::cast(break_locations));
6665}
6666
6667
6668// Set a break point in a function
6669// args[0]: function
6670// args[1]: number: break source position (within the function source)
6671// args[2]: number: break point object
6672static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6673 HandleScope scope;
6674 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006675 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6676 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006677 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6678 RUNTIME_ASSERT(source_position >= 0);
6679 Handle<Object> break_point_object_arg = args.at<Object>(2);
6680
6681 // Set break point.
6682 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6683
6684 return Heap::undefined_value();
6685}
6686
6687
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006688Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6689 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006690 // Iterate the heap looking for SharedFunctionInfo generated from the
6691 // script. The inner most SharedFunctionInfo containing the source position
6692 // for the requested break point is found.
6693 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6694 // which is found is not compiled it is compiled and the heap is iterated
6695 // again as the compilation might create inner functions from the newly
6696 // compiled function and the actual requested break point might be in one of
6697 // these functions.
6698 bool done = false;
6699 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006700 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006701 Handle<SharedFunctionInfo> target;
6702 // The current candidate for the last function in script:
6703 Handle<SharedFunctionInfo> last;
6704 while (!done) {
6705 HeapIterator iterator;
6706 while (iterator.has_next()) {
6707 HeapObject* obj = iterator.next();
6708 ASSERT(obj != NULL);
6709 if (obj->IsSharedFunctionInfo()) {
6710 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6711 if (shared->script() == *script) {
6712 // If the SharedFunctionInfo found has the requested script data and
6713 // contains the source position it is a candidate.
6714 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006715 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006716 start_position = shared->start_position();
6717 }
6718 if (start_position <= position &&
6719 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006720 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006721 // candidate this is the new candidate.
6722 if (target.is_null()) {
6723 target_start_position = start_position;
6724 target = shared;
6725 } else {
6726 if (target_start_position < start_position &&
6727 shared->end_position() < target->end_position()) {
6728 target_start_position = start_position;
6729 target = shared;
6730 }
6731 }
6732 }
6733
6734 // Keep track of the last function in the script.
6735 if (last.is_null() ||
6736 shared->end_position() > last->start_position()) {
6737 last = shared;
6738 }
6739 }
6740 }
6741 }
6742
6743 // Make sure some candidate is selected.
6744 if (target.is_null()) {
6745 if (!last.is_null()) {
6746 // Position after the last function - use last.
6747 target = last;
6748 } else {
6749 // Unable to find function - possibly script without any function.
6750 return Heap::undefined_value();
6751 }
6752 }
6753
6754 // If the candidate found is compiled we are done. NOTE: when lazy
6755 // compilation of inner functions is introduced some additional checking
6756 // needs to be done here to compile inner functions.
6757 done = target->is_compiled();
6758 if (!done) {
6759 // If the candidate is not compiled compile it to reveal any inner
6760 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006761 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006762 }
6763 }
6764
6765 return *target;
6766}
6767
6768
6769// Change the state of a break point in a script. NOTE: Regarding performance
6770// see the NOTE for GetScriptFromScriptData.
6771// args[0]: script to set break point in
6772// args[1]: number: break source position (within the script source)
6773// args[2]: number: break point object
6774static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6775 HandleScope scope;
6776 ASSERT(args.length() == 3);
6777 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6778 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6779 RUNTIME_ASSERT(source_position >= 0);
6780 Handle<Object> break_point_object_arg = args.at<Object>(2);
6781
6782 // Get the script from the script wrapper.
6783 RUNTIME_ASSERT(wrapper->value()->IsScript());
6784 Handle<Script> script(Script::cast(wrapper->value()));
6785
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006786 Object* result = Runtime::FindSharedFunctionInfoInScript(
6787 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006788 if (!result->IsUndefined()) {
6789 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6790 // Find position within function. The script position might be before the
6791 // source position of the first function.
6792 int position;
6793 if (shared->start_position() > source_position) {
6794 position = 0;
6795 } else {
6796 position = source_position - shared->start_position();
6797 }
6798 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6799 }
6800 return Heap::undefined_value();
6801}
6802
6803
6804// Clear a break point
6805// args[0]: number: break point object
6806static Object* Runtime_ClearBreakPoint(Arguments args) {
6807 HandleScope scope;
6808 ASSERT(args.length() == 1);
6809 Handle<Object> break_point_object_arg = args.at<Object>(0);
6810
6811 // Clear break point.
6812 Debug::ClearBreakPoint(break_point_object_arg);
6813
6814 return Heap::undefined_value();
6815}
6816
6817
6818// Change the state of break on exceptions
6819// args[0]: boolean indicating uncaught exceptions
6820// args[1]: boolean indicating on/off
6821static Object* Runtime_ChangeBreakOnException(Arguments args) {
6822 HandleScope scope;
6823 ASSERT(args.length() == 2);
6824 ASSERT(args[0]->IsNumber());
6825 ASSERT(args[1]->IsBoolean());
6826
6827 // Update break point state
6828 ExceptionBreakType type =
6829 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6830 bool enable = args[1]->ToBoolean()->IsTrue();
6831 Debug::ChangeBreakOnException(type, enable);
6832 return Heap::undefined_value();
6833}
6834
6835
6836// Prepare for stepping
6837// args[0]: break id for checking execution state
6838// args[1]: step action from the enumeration StepAction
6839// args[2]: number of times to perform the step
6840static Object* Runtime_PrepareStep(Arguments args) {
6841 HandleScope scope;
6842 ASSERT(args.length() == 3);
6843 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006844 Object* check = Runtime_CheckExecutionState(args);
6845 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006846 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6847 return Top::Throw(Heap::illegal_argument_symbol());
6848 }
6849
6850 // Get the step action and check validity.
6851 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6852 if (step_action != StepIn &&
6853 step_action != StepNext &&
6854 step_action != StepOut &&
6855 step_action != StepInMin &&
6856 step_action != StepMin) {
6857 return Top::Throw(Heap::illegal_argument_symbol());
6858 }
6859
6860 // Get the number of steps.
6861 int step_count = NumberToInt32(args[2]);
6862 if (step_count < 1) {
6863 return Top::Throw(Heap::illegal_argument_symbol());
6864 }
6865
6866 // Prepare step.
6867 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6868 return Heap::undefined_value();
6869}
6870
6871
6872// Clear all stepping set by PrepareStep.
6873static Object* Runtime_ClearStepping(Arguments args) {
6874 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006875 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006876 Debug::ClearStepping();
6877 return Heap::undefined_value();
6878}
6879
6880
6881// Creates a copy of the with context chain. The copy of the context chain is
6882// is linked to the function context supplied.
6883static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6884 Handle<Context> function_context) {
6885 // At the bottom of the chain. Return the function context to link to.
6886 if (context_chain->is_function_context()) {
6887 return function_context;
6888 }
6889
6890 // Recursively copy the with contexts.
6891 Handle<Context> previous(context_chain->previous());
6892 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6893 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006894 CopyWithContextChain(function_context, previous),
6895 extension,
6896 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006897}
6898
6899
6900// Helper function to find or create the arguments object for
6901// Runtime_DebugEvaluate.
6902static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6903 Handle<JSFunction> function,
6904 Handle<Code> code,
6905 const ScopeInfo<>* sinfo,
6906 Handle<Context> function_context) {
6907 // Try to find the value of 'arguments' to pass as parameter. If it is not
6908 // found (that is the debugged function does not reference 'arguments' and
6909 // does not support eval) then create an 'arguments' object.
6910 int index;
6911 if (sinfo->number_of_stack_slots() > 0) {
6912 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6913 if (index != -1) {
6914 return Handle<Object>(frame->GetExpression(index));
6915 }
6916 }
6917
6918 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6919 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6920 NULL);
6921 if (index != -1) {
6922 return Handle<Object>(function_context->get(index));
6923 }
6924 }
6925
6926 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006927 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6928 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006929 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006930 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006931 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006933 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006934 return arguments;
6935}
6936
6937
6938// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006939// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006940// extension part has all the parameters and locals of the function on the
6941// stack frame. A function which calls eval with the code to evaluate is then
6942// compiled in this context and called in this context. As this context
6943// replaces the context of the function on the stack frame a new (empty)
6944// function is created as well to be used as the closure for the context.
6945// This function and the context acts as replacements for the function on the
6946// stack frame presenting the same view of the values of parameters and
6947// local variables as if the piece of JavaScript was evaluated at the point
6948// where the function on the stack frame is currently stopped.
6949static Object* Runtime_DebugEvaluate(Arguments args) {
6950 HandleScope scope;
6951
6952 // Check the execution state and decode arguments frame and source to be
6953 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006954 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006955 Object* check_result = Runtime_CheckExecutionState(args);
6956 if (check_result->IsFailure()) return check_result;
6957 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6958 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006959 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6960
6961 // Handle the processing of break.
6962 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006963
6964 // Get the frame where the debugging is performed.
6965 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6966 JavaScriptFrameIterator it(id);
6967 JavaScriptFrame* frame = it.frame();
6968 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6969 Handle<Code> code(function->code());
6970 ScopeInfo<> sinfo(*code);
6971
6972 // Traverse the saved contexts chain to find the active context for the
6973 // selected frame.
6974 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006975 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006976 save = save->prev();
6977 }
6978 ASSERT(save != NULL);
6979 SaveContext savex;
6980 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006981
6982 // Create the (empty) function replacing the function on the stack frame for
6983 // the purpose of evaluating in the context created below. It is important
6984 // that this function does not describe any parameters and local variables
6985 // in the context. If it does then this will cause problems with the lookup
6986 // in Context::Lookup, where context slots for parameters and local variables
6987 // are looked at before the extension object.
6988 Handle<JSFunction> go_between =
6989 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6990 go_between->set_context(function->context());
6991#ifdef DEBUG
6992 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6993 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6994 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6995#endif
6996
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006997 // Materialize the content of the local scope into a JSObject.
6998 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006999
7000 // Allocate a new context for the debug evaluation and set the extension
7001 // object build.
7002 Handle<Context> context =
7003 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007004 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007005 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007006 Handle<Context> frame_context(Context::cast(frame->context()));
7007 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007008 context = CopyWithContextChain(frame_context, context);
7009
7010 // Wrap the evaluation statement in a new function compiled in the newly
7011 // created context. The function has one parameter which has to be called
7012 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007013 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007014 // function(arguments,__source__) {return eval(__source__);}
7015 static const char* source_str =
7016 "function(arguments,__source__){return eval(__source__);}";
7017 static const int source_str_length = strlen(source_str);
7018 Handle<String> function_source =
7019 Factory::NewStringFromAscii(Vector<const char>(source_str,
7020 source_str_length));
7021 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007022 Compiler::CompileEval(function_source,
7023 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007024 context->IsGlobalContext(),
7025 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007026 if (boilerplate.is_null()) return Failure::Exception();
7027 Handle<JSFunction> compiled_function =
7028 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7029
7030 // Invoke the result of the compilation to get the evaluation function.
7031 bool has_pending_exception;
7032 Handle<Object> receiver(frame->receiver());
7033 Handle<Object> evaluation_function =
7034 Execution::Call(compiled_function, receiver, 0, NULL,
7035 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007036 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007037
7038 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7039 function_context);
7040
7041 // Invoke the evaluation function and return the result.
7042 const int argc = 2;
7043 Object** argv[argc] = { arguments.location(),
7044 Handle<Object>::cast(source).location() };
7045 Handle<Object> result =
7046 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7047 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007048 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007049
7050 // Skip the global proxy as it has no properties and always delegates to the
7051 // real global object.
7052 if (result->IsJSGlobalProxy()) {
7053 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7054 }
7055
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007056 return *result;
7057}
7058
7059
7060static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7061 HandleScope scope;
7062
7063 // Check the execution state and decode arguments frame and source to be
7064 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007065 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007066 Object* check_result = Runtime_CheckExecutionState(args);
7067 if (check_result->IsFailure()) return check_result;
7068 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007069 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7070
7071 // Handle the processing of break.
7072 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007073
7074 // Enter the top context from before the debugger was invoked.
7075 SaveContext save;
7076 SaveContext* top = &save;
7077 while (top != NULL && *top->context() == *Debug::debug_context()) {
7078 top = top->prev();
7079 }
7080 if (top != NULL) {
7081 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007082 }
7083
7084 // Get the global context now set to the top context from before the
7085 // debugger was invoked.
7086 Handle<Context> context = Top::global_context();
7087
7088 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007089 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007090 Handle<JSFunction>(Compiler::CompileEval(source,
7091 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007092 true,
7093 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007094 if (boilerplate.is_null()) return Failure::Exception();
7095 Handle<JSFunction> compiled_function =
7096 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7097 context));
7098
7099 // Invoke the result of the compilation to get the evaluation function.
7100 bool has_pending_exception;
7101 Handle<Object> receiver = Top::global();
7102 Handle<Object> result =
7103 Execution::Call(compiled_function, receiver, 0, NULL,
7104 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007105 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007106 return *result;
7107}
7108
7109
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007110static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7111 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007112 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007113
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007114 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007115 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007116
7117 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007118 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007119 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7120 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7121 // because using
7122 // instances->set(i, *GetScriptWrapper(script))
7123 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7124 // already have deferenced the instances handle.
7125 Handle<JSValue> wrapper = GetScriptWrapper(script);
7126 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127 }
7128
7129 // Return result as a JS array.
7130 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7131 Handle<JSArray>::cast(result)->SetContent(*instances);
7132 return *result;
7133}
7134
7135
7136// Helper function used by Runtime_DebugReferencedBy below.
7137static int DebugReferencedBy(JSObject* target,
7138 Object* instance_filter, int max_references,
7139 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007140 JSFunction* arguments_function) {
7141 NoHandleAllocation ha;
7142 AssertNoAllocation no_alloc;
7143
7144 // Iterate the heap.
7145 int count = 0;
7146 JSObject* last = NULL;
7147 HeapIterator iterator;
7148 while (iterator.has_next() &&
7149 (max_references == 0 || count < max_references)) {
7150 // Only look at all JSObjects.
7151 HeapObject* heap_obj = iterator.next();
7152 if (heap_obj->IsJSObject()) {
7153 // Skip context extension objects and argument arrays as these are
7154 // checked in the context of functions using them.
7155 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007156 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007157 obj->map()->constructor() == arguments_function) {
7158 continue;
7159 }
7160
7161 // Check if the JS object has a reference to the object looked for.
7162 if (obj->ReferencesObject(target)) {
7163 // Check instance filter if supplied. This is normally used to avoid
7164 // references from mirror objects (see Runtime_IsInPrototypeChain).
7165 if (!instance_filter->IsUndefined()) {
7166 Object* V = obj;
7167 while (true) {
7168 Object* prototype = V->GetPrototype();
7169 if (prototype->IsNull()) {
7170 break;
7171 }
7172 if (instance_filter == prototype) {
7173 obj = NULL; // Don't add this object.
7174 break;
7175 }
7176 V = prototype;
7177 }
7178 }
7179
7180 if (obj != NULL) {
7181 // Valid reference found add to instance array if supplied an update
7182 // count.
7183 if (instances != NULL && count < instances_size) {
7184 instances->set(count, obj);
7185 }
7186 last = obj;
7187 count++;
7188 }
7189 }
7190 }
7191 }
7192
7193 // Check for circular reference only. This can happen when the object is only
7194 // referenced from mirrors and has a circular reference in which case the
7195 // object is not really alive and would have been garbage collected if not
7196 // referenced from the mirror.
7197 if (count == 1 && last == target) {
7198 count = 0;
7199 }
7200
7201 // Return the number of referencing objects found.
7202 return count;
7203}
7204
7205
7206// Scan the heap for objects with direct references to an object
7207// args[0]: the object to find references to
7208// args[1]: constructor function for instances to exclude (Mirror)
7209// args[2]: the the maximum number of objects to return
7210static Object* Runtime_DebugReferencedBy(Arguments args) {
7211 ASSERT(args.length() == 3);
7212
7213 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007214 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007215
7216 // Check parameters.
7217 CONVERT_CHECKED(JSObject, target, args[0]);
7218 Object* instance_filter = args[1];
7219 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7220 instance_filter->IsJSObject());
7221 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7222 RUNTIME_ASSERT(max_references >= 0);
7223
7224 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007225 JSObject* arguments_boilerplate =
7226 Top::context()->global_context()->arguments_boilerplate();
7227 JSFunction* arguments_function =
7228 JSFunction::cast(arguments_boilerplate->map()->constructor());
7229
7230 // Get the number of referencing objects.
7231 int count;
7232 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007233 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007234
7235 // Allocate an array to hold the result.
7236 Object* object = Heap::AllocateFixedArray(count);
7237 if (object->IsFailure()) return object;
7238 FixedArray* instances = FixedArray::cast(object);
7239
7240 // Fill the referencing objects.
7241 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007242 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007243
7244 // Return result as JS array.
7245 Object* result =
7246 Heap::AllocateJSObject(
7247 Top::context()->global_context()->array_function());
7248 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7249 return result;
7250}
7251
7252
7253// Helper function used by Runtime_DebugConstructedBy below.
7254static int DebugConstructedBy(JSFunction* constructor, int max_references,
7255 FixedArray* instances, int instances_size) {
7256 AssertNoAllocation no_alloc;
7257
7258 // Iterate the heap.
7259 int count = 0;
7260 HeapIterator iterator;
7261 while (iterator.has_next() &&
7262 (max_references == 0 || count < max_references)) {
7263 // Only look at all JSObjects.
7264 HeapObject* heap_obj = iterator.next();
7265 if (heap_obj->IsJSObject()) {
7266 JSObject* obj = JSObject::cast(heap_obj);
7267 if (obj->map()->constructor() == constructor) {
7268 // Valid reference found add to instance array if supplied an update
7269 // count.
7270 if (instances != NULL && count < instances_size) {
7271 instances->set(count, obj);
7272 }
7273 count++;
7274 }
7275 }
7276 }
7277
7278 // Return the number of referencing objects found.
7279 return count;
7280}
7281
7282
7283// Scan the heap for objects constructed by a specific function.
7284// args[0]: the constructor to find instances of
7285// args[1]: the the maximum number of objects to return
7286static Object* Runtime_DebugConstructedBy(Arguments args) {
7287 ASSERT(args.length() == 2);
7288
7289 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007290 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007291
7292 // Check parameters.
7293 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7294 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7295 RUNTIME_ASSERT(max_references >= 0);
7296
7297 // Get the number of referencing objects.
7298 int count;
7299 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7300
7301 // Allocate an array to hold the result.
7302 Object* object = Heap::AllocateFixedArray(count);
7303 if (object->IsFailure()) return object;
7304 FixedArray* instances = FixedArray::cast(object);
7305
7306 // Fill the referencing objects.
7307 count = DebugConstructedBy(constructor, max_references, instances, count);
7308
7309 // Return result as JS array.
7310 Object* result =
7311 Heap::AllocateJSObject(
7312 Top::context()->global_context()->array_function());
7313 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7314 return result;
7315}
7316
7317
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007318// Find the effective prototype object as returned by __proto__.
7319// args[0]: the object to find the prototype for.
7320static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007321 ASSERT(args.length() == 1);
7322
7323 CONVERT_CHECKED(JSObject, obj, args[0]);
7324
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007325 // Use the __proto__ accessor.
7326 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007327}
7328
7329
7330static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007331 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007332 CPU::DebugBreak();
7333 return Heap::undefined_value();
7334}
7335
7336
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007337static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
7338#ifdef DEBUG
7339 HandleScope scope;
7340 ASSERT(args.length() == 1);
7341 // Get the function and make sure it is compiled.
7342 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7343 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7344 return Failure::Exception();
7345 }
7346 func->code()->PrintLn();
7347#endif // DEBUG
7348 return Heap::undefined_value();
7349}
ager@chromium.org9085a012009-05-11 19:22:57 +00007350
7351
7352static Object* Runtime_FunctionGetInferredName(Arguments args) {
7353 NoHandleAllocation ha;
7354 ASSERT(args.length() == 1);
7355
7356 CONVERT_CHECKED(JSFunction, f, args[0]);
7357 return f->shared()->inferred_name();
7358}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007359#endif // ENABLE_DEBUGGER_SUPPORT
7360
7361
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007362// Finds the script object from the script data. NOTE: This operation uses
7363// heap traversal to find the function generated for the source position
7364// for the requested break point. For lazily compiled functions several heap
7365// traversals might be required rendering this operation as a rather slow
7366// operation. However for setting break points which is normally done through
7367// some kind of user interaction the performance is not crucial.
7368static Handle<Object> Runtime_GetScriptFromScriptName(
7369 Handle<String> script_name) {
7370 // Scan the heap for Script objects to find the script with the requested
7371 // script data.
7372 Handle<Script> script;
7373 HeapIterator iterator;
7374 while (script.is_null() && iterator.has_next()) {
7375 HeapObject* obj = iterator.next();
7376 // If a script is found check if it has the script data requested.
7377 if (obj->IsScript()) {
7378 if (Script::cast(obj)->name()->IsString()) {
7379 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7380 script = Handle<Script>(Script::cast(obj));
7381 }
7382 }
7383 }
7384 }
7385
7386 // If no script with the requested script data is found return undefined.
7387 if (script.is_null()) return Factory::undefined_value();
7388
7389 // Return the script found.
7390 return GetScriptWrapper(script);
7391}
7392
7393
7394// Get the script object from script data. NOTE: Regarding performance
7395// see the NOTE for GetScriptFromScriptData.
7396// args[0]: script data for the script to find the source for
7397static Object* Runtime_GetScript(Arguments args) {
7398 HandleScope scope;
7399
7400 ASSERT(args.length() == 1);
7401
7402 CONVERT_CHECKED(String, script_name, args[0]);
7403
7404 // Find the requested script.
7405 Handle<Object> result =
7406 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7407 return *result;
7408}
7409
7410
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007411static Object* Runtime_Abort(Arguments args) {
7412 ASSERT(args.length() == 2);
7413 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7414 Smi::cast(args[1])->value());
7415 Top::PrintStack();
7416 OS::Abort();
7417 UNREACHABLE();
7418 return NULL;
7419}
7420
7421
kasper.lund44510672008-07-25 07:37:58 +00007422#ifdef DEBUG
7423// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7424// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007425static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007426 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007427 HandleScope scope;
7428 Handle<JSArray> result = Factory::NewJSArray(0);
7429 int index = 0;
7430#define ADD_ENTRY(Name, argc) \
7431 { \
7432 HandleScope inner; \
7433 Handle<String> name = \
7434 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7435 Handle<JSArray> pair = Factory::NewJSArray(0); \
7436 SetElement(pair, 0, name); \
7437 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7438 SetElement(result, index++, pair); \
7439 }
7440 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7441#undef ADD_ENTRY
7442 return *result;
7443}
kasper.lund44510672008-07-25 07:37:58 +00007444#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007445
7446
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007447static Object* Runtime_Log(Arguments args) {
7448 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007449 CONVERT_CHECKED(String, format, args[0]);
7450 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007451 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007452 Logger::LogRuntime(chars, elms);
7453 return Heap::undefined_value();
7454}
7455
7456
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007457static Object* Runtime_IS_VAR(Arguments args) {
7458 UNREACHABLE(); // implemented as macro in the parser
7459 return NULL;
7460}
7461
7462
7463// ----------------------------------------------------------------------------
7464// Implementation of Runtime
7465
7466#define F(name, nargs) \
7467 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7468 static_cast<int>(Runtime::k##name) },
7469
7470static Runtime::Function Runtime_functions[] = {
7471 RUNTIME_FUNCTION_LIST(F)
7472 { NULL, NULL, NULL, 0, -1 }
7473};
7474
7475#undef F
7476
7477
7478Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7479 ASSERT(0 <= fid && fid < kNofFunctions);
7480 return &Runtime_functions[fid];
7481}
7482
7483
7484Runtime::Function* Runtime::FunctionForName(const char* name) {
7485 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7486 if (strcmp(f->name, name) == 0) {
7487 return f;
7488 }
7489 }
7490 return NULL;
7491}
7492
7493
7494void Runtime::PerformGC(Object* result) {
7495 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007496 if (failure->IsRetryAfterGC()) {
7497 // Try to do a garbage collection; ignore it if it fails. The C
7498 // entry stub will throw an out-of-memory exception in that case.
7499 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7500 } else {
7501 // Handle last resort GC and make sure to allow future allocations
7502 // to grow the heap without causing GCs (if possible).
7503 Counters::gc_last_resort_from_js.Increment();
7504 Heap::CollectAllGarbage();
7505 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007506}
7507
7508
7509} } // namespace v8::internal