blob: ad02f5d5b400d34ff6942f20c174c12ee40dadf7 [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
53#define RUNTIME_ASSERT(value) do { \
54 if (!(value)) return IllegalOperation(); \
55} while (false)
56
57// Cast the given object to a value of the specified type and store
58// it in a variable with the given name. If the object is not of the
59// expected type call IllegalOperation and return.
60#define CONVERT_CHECKED(Type, name, obj) \
61 RUNTIME_ASSERT(obj->Is##Type()); \
62 Type* name = Type::cast(obj);
63
64#define CONVERT_ARG_CHECKED(Type, name, index) \
65 RUNTIME_ASSERT(args[index]->Is##Type()); \
66 Handle<Type> name = args.at<Type>(index);
67
kasper.lundbd3ec4e2008-07-09 11:06:54 +000068// Cast the given object to a boolean and store it in a variable with
69// the given name. If the object is not a boolean call IllegalOperation
70// and return.
71#define CONVERT_BOOLEAN_CHECKED(name, obj) \
72 RUNTIME_ASSERT(obj->IsBoolean()); \
73 bool name = (obj)->IsTrue();
74
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000075// Cast the given object to a Smi and store its value in an int variable
76// with the given name. If the object is not a Smi call IllegalOperation
77// and return.
78#define CONVERT_SMI_CHECKED(name, obj) \
79 RUNTIME_ASSERT(obj->IsSmi()); \
80 int name = Smi::cast(obj)->value();
81
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000082// Cast the given object to a double and store it in a variable with
83// the given name. If the object is not a number (as opposed to
84// the number not-a-number) call IllegalOperation and return.
85#define CONVERT_DOUBLE_CHECKED(name, obj) \
86 RUNTIME_ASSERT(obj->IsNumber()); \
87 double name = (obj)->Number();
88
89// Call the specified converter on the object *comand store the result in
90// a variable of the specified type with the given name. If the
91// object is not a Number call IllegalOperation and return.
92#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
93 RUNTIME_ASSERT(obj->IsNumber()); \
94 type name = NumberTo##Type(obj);
95
96// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000097static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098
99
100static Object* IllegalOperation() {
101 return Top::Throw(Heap::illegal_access_symbol());
102}
103
104
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000105static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
106 StackLimitCheck check;
107 if (check.HasOverflowed()) return Top::StackOverflow();
108
109 Object* result = Heap::CopyJSObject(boilerplate);
110 if (result->IsFailure()) return result;
111 JSObject* copy = JSObject::cast(result);
112
113 // Deep copy local properties.
114 if (copy->HasFastProperties()) {
115 FixedArray* properties = copy->properties();
116 WriteBarrierMode mode = properties->GetWriteBarrierMode();
117 for (int i = 0; i < properties->length(); i++) {
118 Object* value = properties->get(i);
119 if (value->IsJSObject()) {
120 JSObject* jsObject = JSObject::cast(value);
121 result = DeepCopyBoilerplate(jsObject);
122 if (result->IsFailure()) return result;
123 properties->set(i, result, mode);
124 }
125 }
126 mode = copy->GetWriteBarrierMode();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000127 int nof = copy->map()->inobject_properties();
128 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 Object* value = copy->InObjectPropertyAt(i);
130 if (value->IsJSObject()) {
131 JSObject* jsObject = JSObject::cast(value);
132 result = DeepCopyBoilerplate(jsObject);
133 if (result->IsFailure()) return result;
134 copy->InObjectPropertyAtPut(i, result, mode);
135 }
136 }
137 } else {
138 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
139 if (result->IsFailure()) return result;
140 FixedArray* names = FixedArray::cast(result);
141 copy->GetLocalPropertyNames(names, 0);
142 for (int i = 0; i < names->length(); i++) {
143 ASSERT(names->get(i)->IsString());
144 String* keyString = String::cast(names->get(i));
145 PropertyAttributes attributes =
146 copy->GetLocalPropertyAttribute(keyString);
147 // Only deep copy fields from the object literal expression.
148 // In particular, don't try to copy the length attribute of
149 // an array.
150 if (attributes != NONE) continue;
151 Object* value = copy->GetProperty(keyString, &attributes);
152 ASSERT(!value->IsFailure());
153 if (value->IsJSObject()) {
154 JSObject* jsObject = JSObject::cast(value);
155 result = DeepCopyBoilerplate(jsObject);
156 if (result->IsFailure()) return result;
157 result = copy->SetProperty(keyString, result, NONE);
158 if (result->IsFailure()) return result;
159 }
160 }
161 }
162
163 // Deep copy local elements.
164 if (copy->HasFastElements()) {
165 FixedArray* elements = copy->elements();
166 WriteBarrierMode mode = elements->GetWriteBarrierMode();
167 for (int i = 0; i < elements->length(); i++) {
168 Object* value = elements->get(i);
169 if (value->IsJSObject()) {
170 JSObject* jsObject = JSObject::cast(value);
171 result = DeepCopyBoilerplate(jsObject);
172 if (result->IsFailure()) return result;
173 elements->set(i, result, mode);
174 }
175 }
176 } else {
177 Dictionary* element_dictionary = copy->element_dictionary();
178 int capacity = element_dictionary->Capacity();
179 for (int i = 0; i < capacity; i++) {
180 Object* k = element_dictionary->KeyAt(i);
181 if (element_dictionary->IsKey(k)) {
182 Object* value = element_dictionary->ValueAt(i);
183 if (value->IsJSObject()) {
184 JSObject* jsObject = JSObject::cast(value);
185 result = DeepCopyBoilerplate(jsObject);
186 if (result->IsFailure()) return result;
187 element_dictionary->ValueAtPut(i, result);
188 }
189 }
190 }
191 }
192 return copy;
193}
194
195
196static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
197 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
198 return DeepCopyBoilerplate(boilerplate);
199}
200
201
202static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000203 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000204 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000205}
206
207
ager@chromium.org236ad962008-09-25 09:45:57 +0000208static Handle<Map> ComputeObjectLiteralMap(
209 Handle<Context> context,
210 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000211 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000212 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000213 if (FLAG_canonicalize_object_literal_maps) {
214 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000215 int number_of_symbol_keys = 0;
216 while ((number_of_symbol_keys < number_of_properties) &&
217 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
218 number_of_symbol_keys++;
219 }
220 // Based on the number of prefix symbols key we decide whether
221 // to use the map cache in the global context.
222 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000223 if ((number_of_symbol_keys == number_of_properties) &&
224 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000225 // Create the fixed array with the key.
226 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
227 for (int i = 0; i < number_of_symbol_keys; i++) {
228 keys->set(i, constant_properties->get(i*2));
229 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000230 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000231 return Factory::ObjectLiteralMapFromCache(context, keys);
232 }
233 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000234 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000235 return Factory::CopyMap(
236 Handle<Map>(context->object_function()->initial_map()),
237 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000238}
239
240
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000241static Handle<Object> CreateLiteralBoilerplate(
242 Handle<FixedArray> literals,
243 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000244
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000245
246static Handle<Object> CreateObjectLiteralBoilerplate(
247 Handle<FixedArray> literals,
248 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000249 // Get the global context from the literals array. This is the
250 // context in which the function was created and we use the object
251 // function from this context to create the object literal. We do
252 // not use the object function from the current global context
253 // because this might be the object function from another context
254 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000255 Handle<Context> context =
256 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
257
258 bool is_result_from_cache;
259 Handle<Map> map = ComputeObjectLiteralMap(context,
260 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000261 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000262
ager@chromium.org236ad962008-09-25 09:45:57 +0000263 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000264 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000265 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000266 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
267 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000268 for (int index = 0; index < length; index +=2) {
269 Handle<Object> key(constant_properties->get(index+0));
270 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000271 if (value->IsFixedArray()) {
272 // The value contains the constant_properties of a
273 // simple object literal.
274 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
275 value = CreateLiteralBoilerplate(literals, array);
276 if (value.is_null()) return value;
277 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000278 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 uint32_t element_index = 0;
280 if (key->IsSymbol()) {
281 // If key is a symbol it is not an array element.
282 Handle<String> name(String::cast(*key));
283 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000284 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000285 } else if (Array::IndexFromObject(*key, &element_index)) {
286 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 } else {
289 // Non-uint32 number.
290 ASSERT(key->IsNumber());
291 double num = key->Number();
292 char arr[100];
293 Vector<char> buffer(arr, ARRAY_SIZE(arr));
294 const char* str = DoubleToCString(num, buffer);
295 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000298 // If setting the property on the boilerplate throws an
299 // exception, the exception is converted to an empty handle in
300 // the handle based operations. In that case, we need to
301 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000302 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303 }
304 }
305
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000306 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000307}
308
309
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000310static Handle<Object> CreateArrayLiteralBoilerplate(
311 Handle<FixedArray> literals,
312 Handle<FixedArray> elements) {
313 // Create the JSArray.
314 Handle<JSFunction> constructor(
315 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
316 Handle<Object> object = Factory::NewJSObject(constructor);
317
318 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
319
320 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
321 for (int i = 0; i < content->length(); i++) {
322 if (content->get(i)->IsFixedArray()) {
323 // The value contains the constant_properties of a
324 // simple object literal.
325 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
326 Handle<Object> result =
327 CreateLiteralBoilerplate(literals, fa);
328 if (result.is_null()) return result;
329 content->set(i, *result);
330 }
331 }
332
333 // Set the elements.
334 Handle<JSArray>::cast(object)->SetContent(*content);
335 return object;
336}
337
338
339static Handle<Object> CreateLiteralBoilerplate(
340 Handle<FixedArray> literals,
341 Handle<FixedArray> array) {
342 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
343 switch (CompileTimeValue::GetType(array)) {
344 case CompileTimeValue::OBJECT_LITERAL:
345 return CreateObjectLiteralBoilerplate(literals, elements);
346 case CompileTimeValue::ARRAY_LITERAL:
347 return CreateArrayLiteralBoilerplate(literals, elements);
348 default:
349 UNREACHABLE();
350 return Handle<Object>::null();
351 }
352}
353
354
355static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
356 HandleScope scope;
357 ASSERT(args.length() == 3);
358 // Copy the arguments.
359 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
360 CONVERT_SMI_CHECKED(literals_index, args[1]);
361 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
362
363 Handle<Object> result =
364 CreateObjectLiteralBoilerplate(literals, constant_properties);
365
366 if (result.is_null()) return Failure::Exception();
367
368 // Update the functions literal and return the boilerplate.
369 literals->set(literals_index, *result);
370
371 return *result;
372}
373
374
375static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000376 // Takes a FixedArray of elements containing the literal elements of
377 // the array literal and produces JSArray with those elements.
378 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000379 // which contains the context from which to get the Array function
380 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 HandleScope scope;
382 ASSERT(args.length() == 3);
383 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
384 CONVERT_SMI_CHECKED(literals_index, args[1]);
385 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000386
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000387 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
388 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000389
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000390 // Update the functions literal and return the boilerplate.
391 literals->set(literals_index, *object);
392 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000393}
394
395
ager@chromium.org32912102009-01-16 10:38:43 +0000396static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
397 ASSERT(args.length() == 2);
398 CONVERT_CHECKED(String, key, args[0]);
399 Object* value = args[1];
400 // Create a catch context extension object.
401 JSFunction* constructor =
402 Top::context()->global_context()->context_extension_function();
403 Object* object = Heap::AllocateJSObject(constructor);
404 if (object->IsFailure()) return object;
405 // Assign the exception value to the catch variable and make sure
406 // that the catch variable is DontDelete.
407 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
408 if (value->IsFailure()) return value;
409 return object;
410}
411
412
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000413static Object* Runtime_ClassOf(Arguments args) {
414 NoHandleAllocation ha;
415 ASSERT(args.length() == 1);
416 Object* obj = args[0];
417 if (!obj->IsJSObject()) return Heap::null_value();
418 return JSObject::cast(obj)->class_name();
419}
420
ager@chromium.org7c537e22008-10-16 08:43:32 +0000421
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000422static Object* Runtime_HasStringClass(Arguments args) {
423 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000424}
425
426
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000427static Object* Runtime_HasDateClass(Arguments args) {
428 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000429}
430
431
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000432static Object* Runtime_HasArrayClass(Arguments args) {
433 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
434}
435
436
437static Object* Runtime_HasFunctionClass(Arguments args) {
438 return Heap::ToBoolean(
439 args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
440}
441
442
443static Object* Runtime_HasNumberClass(Arguments args) {
444 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
445}
446
447
448static Object* Runtime_HasBooleanClass(Arguments args) {
449 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
450}
451
452
453static Object* Runtime_HasArgumentsClass(Arguments args) {
454 return Heap::ToBoolean(
455 args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
456}
457
458
459static Object* Runtime_HasRegExpClass(Arguments args) {
460 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000461}
462
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000463
464static Object* Runtime_IsInPrototypeChain(Arguments args) {
465 NoHandleAllocation ha;
466 ASSERT(args.length() == 2);
467 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
468 Object* O = args[0];
469 Object* V = args[1];
470 while (true) {
471 Object* prototype = V->GetPrototype();
472 if (prototype->IsNull()) return Heap::false_value();
473 if (O == prototype) return Heap::true_value();
474 V = prototype;
475 }
476}
477
478
ager@chromium.org9085a012009-05-11 19:22:57 +0000479// Inserts an object as the hidden prototype of another object.
480static Object* Runtime_SetHiddenPrototype(Arguments args) {
481 NoHandleAllocation ha;
482 ASSERT(args.length() == 2);
483 CONVERT_CHECKED(JSObject, jsobject, args[0]);
484 CONVERT_CHECKED(JSObject, proto, args[1]);
485
486 // Sanity checks. The old prototype (that we are replacing) could
487 // theoretically be null, but if it is not null then check that we
488 // didn't already install a hidden prototype here.
489 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
490 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
491 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
492
493 // Allocate up front before we start altering state in case we get a GC.
494 Object* map_or_failure = proto->map()->CopyDropTransitions();
495 if (map_or_failure->IsFailure()) return map_or_failure;
496 Map* new_proto_map = Map::cast(map_or_failure);
497
498 map_or_failure = jsobject->map()->CopyDropTransitions();
499 if (map_or_failure->IsFailure()) return map_or_failure;
500 Map* new_map = Map::cast(map_or_failure);
501
502 // Set proto's prototype to be the old prototype of the object.
503 new_proto_map->set_prototype(jsobject->GetPrototype());
504 proto->set_map(new_proto_map);
505 new_proto_map->set_is_hidden_prototype();
506
507 // Set the object's prototype to proto.
508 new_map->set_prototype(proto);
509 jsobject->set_map(new_map);
510
511 return Heap::undefined_value();
512}
513
514
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000515static Object* Runtime_IsConstructCall(Arguments args) {
516 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000517 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000518 JavaScriptFrameIterator it;
519 return Heap::ToBoolean(it.frame()->IsConstructor());
520}
521
522
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000523static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000524 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000526 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
527 CONVERT_ARG_CHECKED(String, pattern, 1);
528 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000529 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
530 if (result.is_null()) return Failure::Exception();
531 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000532}
533
534
535static Object* Runtime_CreateApiFunction(Arguments args) {
536 HandleScope scope;
537 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000538 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000539 return *Factory::CreateApiFunction(data);
540}
541
542
543static Object* Runtime_IsTemplate(Arguments args) {
544 ASSERT(args.length() == 1);
545 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000546 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000547 return Heap::ToBoolean(result);
548}
549
550
551static Object* Runtime_GetTemplateField(Arguments args) {
552 ASSERT(args.length() == 2);
553 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000554 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000555 int index = field->value();
556 int offset = index * kPointerSize + HeapObject::kHeaderSize;
557 InstanceType type = templ->map()->instance_type();
558 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
559 type == OBJECT_TEMPLATE_INFO_TYPE);
560 RUNTIME_ASSERT(offset > 0);
561 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
562 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
563 } else {
564 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
565 }
566 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000567}
568
569
ager@chromium.org870a0b62008-11-04 11:43:05 +0000570static Object* Runtime_DisableAccessChecks(Arguments args) {
571 ASSERT(args.length() == 1);
572 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000573 Map* old_map = object->map();
574 bool needs_access_checks = old_map->is_access_check_needed();
575 if (needs_access_checks) {
576 // Copy map so it won't interfere constructor's initial map.
577 Object* new_map = old_map->CopyDropTransitions();
578 if (new_map->IsFailure()) return new_map;
579
580 Map::cast(new_map)->set_is_access_check_needed(false);
581 object->set_map(Map::cast(new_map));
582 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000583 return needs_access_checks ? Heap::true_value() : Heap::false_value();
584}
585
586
587static Object* Runtime_EnableAccessChecks(Arguments args) {
588 ASSERT(args.length() == 1);
589 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000590 Map* old_map = object->map();
591 if (!old_map->is_access_check_needed()) {
592 // Copy map so it won't interfere constructor's initial map.
593 Object* new_map = old_map->CopyDropTransitions();
594 if (new_map->IsFailure()) return new_map;
595
596 Map::cast(new_map)->set_is_access_check_needed(true);
597 object->set_map(Map::cast(new_map));
598 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000599 return Heap::undefined_value();
600}
601
602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000603static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
604 HandleScope scope;
605 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
606 Handle<Object> args[2] = { type_handle, name };
607 Handle<Object> error =
608 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
609 return Top::Throw(*error);
610}
611
612
613static Object* Runtime_DeclareGlobals(Arguments args) {
614 HandleScope scope;
615 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
616
617 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
618 Handle<Context> context = args.at<Context>(1);
619 bool is_eval = Smi::cast(args[2])->value() == 1;
620
621 // Compute the property attributes. According to ECMA-262, section
622 // 13, page 71, the property must be read-only and
623 // non-deletable. However, neither SpiderMonkey nor KJS creates the
624 // property as read-only, so we don't either.
625 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
626
627 // Only optimize the object if we intend to add more than 5 properties.
628 OptimizedObjectForAddingMultipleProperties ba(global, pairs->length()/2 > 5);
629
630 // Traverse the name/value pairs and set the properties.
631 int length = pairs->length();
632 for (int i = 0; i < length; i += 2) {
633 HandleScope scope;
634 Handle<String> name(String::cast(pairs->get(i)));
635 Handle<Object> value(pairs->get(i + 1));
636
637 // We have to declare a global const property. To capture we only
638 // assign to it when evaluating the assignment for "const x =
639 // <expr>" the initial value is the hole.
640 bool is_const_property = value->IsTheHole();
641
642 if (value->IsUndefined() || is_const_property) {
643 // Lookup the property in the global object, and don't set the
644 // value of the variable if the property is already there.
645 LookupResult lookup;
646 global->Lookup(*name, &lookup);
647 if (lookup.IsProperty()) {
648 // Determine if the property is local by comparing the holder
649 // against the global object. The information will be used to
650 // avoid throwing re-declaration errors when declaring
651 // variables or constants that exist in the prototype chain.
652 bool is_local = (*global == lookup.holder());
653 // Get the property attributes and determine if the property is
654 // read-only.
655 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
656 bool is_read_only = (attributes & READ_ONLY) != 0;
657 if (lookup.type() == INTERCEPTOR) {
658 // If the interceptor says the property is there, we
659 // just return undefined without overwriting the property.
660 // Otherwise, we continue to setting the property.
661 if (attributes != ABSENT) {
662 // Check if the existing property conflicts with regards to const.
663 if (is_local && (is_read_only || is_const_property)) {
664 const char* type = (is_read_only) ? "const" : "var";
665 return ThrowRedeclarationError(type, name);
666 };
667 // The property already exists without conflicting: Go to
668 // the next declaration.
669 continue;
670 }
671 // Fall-through and introduce the absent property by using
672 // SetProperty.
673 } else {
674 if (is_local && (is_read_only || is_const_property)) {
675 const char* type = (is_read_only) ? "const" : "var";
676 return ThrowRedeclarationError(type, name);
677 }
678 // The property already exists without conflicting: Go to
679 // the next declaration.
680 continue;
681 }
682 }
683 } else {
684 // Copy the function and update its context. Use it as value.
685 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
686 Handle<JSFunction> function =
687 Factory::NewFunctionFromBoilerplate(boilerplate, context);
688 value = function;
689 }
690
691 LookupResult lookup;
692 global->LocalLookup(*name, &lookup);
693
694 PropertyAttributes attributes = is_const_property
695 ? static_cast<PropertyAttributes>(base | READ_ONLY)
696 : base;
697
698 if (lookup.IsProperty()) {
699 // There's a local property that we need to overwrite because
700 // we're either declaring a function or there's an interceptor
701 // that claims the property is absent.
702
703 // Check for conflicting re-declarations. We cannot have
704 // conflicting types in case of intercepted properties because
705 // they are absent.
706 if (lookup.type() != INTERCEPTOR &&
707 (lookup.IsReadOnly() || is_const_property)) {
708 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
709 return ThrowRedeclarationError(type, name);
710 }
711 SetProperty(global, name, value, attributes);
712 } else {
713 // If a property with this name does not already exist on the
714 // global object add the property locally. We take special
715 // precautions to always add it as a local property even in case
716 // of callbacks in the prototype chain (this rules out using
717 // SetProperty). Also, we must use the handle-based version to
718 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000719 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000720 }
721 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000722
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000723 return Heap::undefined_value();
724}
725
726
727static Object* Runtime_DeclareContextSlot(Arguments args) {
728 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000729 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000730
ager@chromium.org7c537e22008-10-16 08:43:32 +0000731 CONVERT_ARG_CHECKED(Context, context, 0);
732 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000734 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000735 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000736 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737
738 // Declarations are always done in the function context.
739 context = Handle<Context>(context->fcontext());
740
741 int index;
742 PropertyAttributes attributes;
743 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000744 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000745 context->Lookup(name, flags, &index, &attributes);
746
747 if (attributes != ABSENT) {
748 // The name was declared before; check for conflicting
749 // re-declarations: This is similar to the code in parser.cc in
750 // the AstBuildingParser::Declare function.
751 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
752 // Functions are not read-only.
753 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
754 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
755 return ThrowRedeclarationError(type, name);
756 }
757
758 // Initialize it if necessary.
759 if (*initial_value != NULL) {
760 if (index >= 0) {
761 // The variable or constant context slot should always be in
762 // the function context; not in any outer context nor in the
763 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000764 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000765 if (((attributes & READ_ONLY) == 0) ||
766 context->get(index)->IsTheHole()) {
767 context->set(index, *initial_value);
768 }
769 } else {
770 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000771 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000772 SetProperty(context_ext, name, initial_value, mode);
773 }
774 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000776 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000777 // The property is not in the function context. It needs to be
778 // "declared" in the function context's extension context, or in the
779 // global context.
780 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000781 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000782 // The function context's extension context exists - use it.
783 context_ext = Handle<JSObject>(context->extension());
784 } else {
785 // The function context's extension context does not exists - allocate
786 // it.
787 context_ext = Factory::NewJSObject(Top::context_extension_function());
788 // And store it in the extension slot.
789 context->set_extension(*context_ext);
790 }
791 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000792
ager@chromium.org7c537e22008-10-16 08:43:32 +0000793 // Declare the property by setting it to the initial value if provided,
794 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
795 // constant declarations).
796 ASSERT(!context_ext->HasLocalProperty(*name));
797 Handle<Object> value(Heap::undefined_value());
798 if (*initial_value != NULL) value = initial_value;
799 SetProperty(context_ext, name, value, mode);
800 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
801 }
802
803 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000804}
805
806
807static Object* Runtime_InitializeVarGlobal(Arguments args) {
808 NoHandleAllocation nha;
809
810 // Determine if we need to assign to the variable if it already
811 // exists (based on the number of arguments).
812 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
813 bool assign = args.length() == 2;
814
815 CONVERT_ARG_CHECKED(String, name, 0);
816 GlobalObject* global = Top::context()->global();
817
818 // According to ECMA-262, section 12.2, page 62, the property must
819 // not be deletable.
820 PropertyAttributes attributes = DONT_DELETE;
821
822 // Lookup the property locally in the global object. If it isn't
823 // there, we add the property and take special precautions to always
824 // add it as a local property even in case of callbacks in the
825 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000826 // We have IgnoreAttributesAndSetLocalProperty for this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 LookupResult lookup;
828 global->LocalLookup(*name, &lookup);
829 if (!lookup.IsProperty()) {
830 Object* value = (assign) ? args[1] : Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000831 return global->IgnoreAttributesAndSetLocalProperty(*name,
832 value,
833 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000834 }
835
836 // Determine if this is a redeclaration of something read-only.
837 if (lookup.IsReadOnly()) {
838 return ThrowRedeclarationError("const", name);
839 }
840
841 // Determine if this is a redeclaration of an intercepted read-only
842 // property and figure out if the property exists at all.
843 bool found = true;
844 PropertyType type = lookup.type();
845 if (type == INTERCEPTOR) {
846 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
847 if (intercepted == ABSENT) {
848 // The interceptor claims the property isn't there. We need to
849 // make sure to introduce it.
850 found = false;
851 } else if ((intercepted & READ_ONLY) != 0) {
852 // The property is present, but read-only. Since we're trying to
853 // overwrite it with a variable declaration we must throw a
854 // re-declaration error.
855 return ThrowRedeclarationError("const", name);
856 }
857 // Restore global object from context (in case of GC).
858 global = Top::context()->global();
859 }
860
861 if (found && !assign) {
862 // The global property is there and we're not assigning any value
863 // to it. Just return.
864 return Heap::undefined_value();
865 }
866
867 // Assign the value (or undefined) to the property.
868 Object* value = (assign) ? args[1] : Heap::undefined_value();
869 return global->SetProperty(&lookup, *name, value, attributes);
870}
871
872
873static Object* Runtime_InitializeConstGlobal(Arguments args) {
874 // All constants are declared with an initial value. The name
875 // of the constant is the first argument and the initial value
876 // is the second.
877 RUNTIME_ASSERT(args.length() == 2);
878 CONVERT_ARG_CHECKED(String, name, 0);
879 Handle<Object> value = args.at<Object>(1);
880
881 // Get the current global object from top.
882 GlobalObject* global = Top::context()->global();
883
884 // According to ECMA-262, section 12.2, page 62, the property must
885 // not be deletable. Since it's a const, it must be READ_ONLY too.
886 PropertyAttributes attributes =
887 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
888
889 // Lookup the property locally in the global object. If it isn't
890 // there, we add the property and take special precautions to always
891 // add it as a local property even in case of callbacks in the
892 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000893 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894 LookupResult lookup;
895 global->LocalLookup(*name, &lookup);
896 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000897 return global->IgnoreAttributesAndSetLocalProperty(*name,
898 *value,
899 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000900 }
901
902 // Determine if this is a redeclaration of something not
903 // read-only. In case the result is hidden behind an interceptor we
904 // need to ask it for the property attributes.
905 if (!lookup.IsReadOnly()) {
906 if (lookup.type() != INTERCEPTOR) {
907 return ThrowRedeclarationError("var", name);
908 }
909
910 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
911
912 // Throw re-declaration error if the intercepted property is present
913 // but not read-only.
914 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
915 return ThrowRedeclarationError("var", name);
916 }
917
918 // Restore global object from context (in case of GC) and continue
919 // with setting the value because the property is either absent or
920 // read-only. We also have to do redo the lookup.
921 global = Top::context()->global();
922
923 // BUG 1213579: Handle the case where we have to set a read-only
924 // property through an interceptor and only do it if it's
925 // uninitialized, e.g. the hole. Nirk...
926 global->SetProperty(*name, *value, attributes);
927 return *value;
928 }
929
930 // Set the value, but only we're assigning the initial value to a
931 // constant. For now, we determine this by checking if the
932 // current value is the hole.
933 PropertyType type = lookup.type();
934 if (type == FIELD) {
935 FixedArray* properties = global->properties();
936 int index = lookup.GetFieldIndex();
937 if (properties->get(index)->IsTheHole()) {
938 properties->set(index, *value);
939 }
940 } else if (type == NORMAL) {
941 Dictionary* dictionary = global->property_dictionary();
942 int entry = lookup.GetDictionaryEntry();
943 if (dictionary->ValueAt(entry)->IsTheHole()) {
944 dictionary->ValueAtPut(entry, *value);
945 }
946 } else {
947 // Ignore re-initialization of constants that have already been
948 // assigned a function value.
949 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
950 }
951
952 // Use the set value as the result of the operation.
953 return *value;
954}
955
956
957static Object* Runtime_InitializeConstContextSlot(Arguments args) {
958 HandleScope scope;
959 ASSERT(args.length() == 3);
960
961 Handle<Object> value(args[0]);
962 ASSERT(!value->IsTheHole());
963 CONVERT_ARG_CHECKED(Context, context, 1);
964 Handle<String> name(String::cast(args[2]));
965
966 // Initializations are always done in the function context.
967 context = Handle<Context>(context->fcontext());
968
969 int index;
970 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000971 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000972 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000973 context->Lookup(name, flags, &index, &attributes);
974
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000975 // In most situations, the property introduced by the const
976 // declaration should be present in the context extension object.
977 // However, because declaration and initialization are separate, the
978 // property might have been deleted (if it was introduced by eval)
979 // before we reach the initialization point.
980 //
981 // Example:
982 //
983 // function f() { eval("delete x; const x;"); }
984 //
985 // In that case, the initialization behaves like a normal assignment
986 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000987 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000988 // Property was found in a context.
989 if (holder->IsContext()) {
990 // The holder cannot be the function context. If it is, there
991 // should have been a const redeclaration error when declaring
992 // the const property.
993 ASSERT(!holder.is_identical_to(context));
994 if ((attributes & READ_ONLY) == 0) {
995 Handle<Context>::cast(holder)->set(index, *value);
996 }
997 } else {
998 // The holder is an arguments object.
999 ASSERT((attributes & READ_ONLY) == 0);
1000 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 }
1002 return *value;
1003 }
1004
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001005 // The property could not be found, we introduce it in the global
1006 // context.
1007 if (attributes == ABSENT) {
1008 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1009 SetProperty(global, name, value, NONE);
1010 return *value;
1011 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001012
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001013 // The property was present in a context extension object.
1014 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001015
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001016 if (*context_ext == context->extension()) {
1017 // This is the property that was introduced by the const
1018 // declaration. Set it if it hasn't been set before. NOTE: We
1019 // cannot use GetProperty() to get the current value as it
1020 // 'unholes' the value.
1021 LookupResult lookup;
1022 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1023 ASSERT(lookup.IsProperty()); // the property was declared
1024 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1025
1026 PropertyType type = lookup.type();
1027 if (type == FIELD) {
1028 FixedArray* properties = context_ext->properties();
1029 int index = lookup.GetFieldIndex();
1030 if (properties->get(index)->IsTheHole()) {
1031 properties->set(index, *value);
1032 }
1033 } else if (type == NORMAL) {
1034 Dictionary* dictionary = context_ext->property_dictionary();
1035 int entry = lookup.GetDictionaryEntry();
1036 if (dictionary->ValueAt(entry)->IsTheHole()) {
1037 dictionary->ValueAtPut(entry, *value);
1038 }
1039 } else {
1040 // We should not reach here. Any real, named property should be
1041 // either a field or a dictionary slot.
1042 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001043 }
1044 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001045 // The property was found in a different context extension object.
1046 // Set it if it is not a read-only property.
1047 if ((attributes & READ_ONLY) == 0) {
1048 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1049 // Setting a property might throw an exception. Exceptions
1050 // are converted to empty handles in handle operations. We
1051 // need to convert back to exceptions here.
1052 if (set.is_null()) {
1053 ASSERT(Top::has_pending_exception());
1054 return Failure::Exception();
1055 }
1056 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001057 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001058
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001059 return *value;
1060}
1061
1062
1063static Object* Runtime_RegExpExec(Arguments args) {
1064 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001065 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001066 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1067 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001068 // Due to the way the JS files are constructed this must be less than the
1069 // length of a string, i.e. it is always a Smi. We check anyway for security.
1070 CONVERT_CHECKED(Smi, index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001071 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001072 RUNTIME_ASSERT(last_match_info->HasFastElements());
1073 RUNTIME_ASSERT(index->value() >= 0);
1074 RUNTIME_ASSERT(index->value() <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001075 Handle<Object> result = RegExpImpl::Exec(regexp,
1076 subject,
1077 index->value(),
1078 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001079 if (result.is_null()) return Failure::Exception();
1080 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001081}
1082
1083
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001084static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1085 HandleScope scope;
1086 ASSERT(args.length() == 4);
1087 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1088 int index = Smi::cast(args[1])->value();
1089 Handle<String> pattern = args.at<String>(2);
1090 Handle<String> flags = args.at<String>(3);
1091
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001092 // Get the RegExp function from the context in the literals array.
1093 // This is the RegExp function from the context in which the
1094 // function was created. We do not use the RegExp function from the
1095 // current global context because this might be the RegExp function
1096 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001097 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001098 Handle<JSFunction>(
1099 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001100 // Compute the regular expression literal.
1101 bool has_pending_exception;
1102 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001103 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1104 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001105 if (has_pending_exception) {
1106 ASSERT(Top::has_pending_exception());
1107 return Failure::Exception();
1108 }
1109 literals->set(index, *regexp);
1110 return *regexp;
1111}
1112
1113
1114static Object* Runtime_FunctionGetName(Arguments args) {
1115 NoHandleAllocation ha;
1116 ASSERT(args.length() == 1);
1117
1118 CONVERT_CHECKED(JSFunction, f, args[0]);
1119 return f->shared()->name();
1120}
1121
1122
ager@chromium.org236ad962008-09-25 09:45:57 +00001123static Object* Runtime_FunctionSetName(Arguments args) {
1124 NoHandleAllocation ha;
1125 ASSERT(args.length() == 2);
1126
1127 CONVERT_CHECKED(JSFunction, f, args[0]);
1128 CONVERT_CHECKED(String, name, args[1]);
1129 f->shared()->set_name(name);
1130 return Heap::undefined_value();
1131}
1132
1133
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001134static Object* Runtime_FunctionGetScript(Arguments args) {
1135 HandleScope scope;
1136 ASSERT(args.length() == 1);
1137
1138 CONVERT_CHECKED(JSFunction, fun, args[0]);
1139 Handle<Object> script = Handle<Object>(fun->shared()->script());
1140 if (!script->IsScript()) return Heap::undefined_value();
1141
1142 return *GetScriptWrapper(Handle<Script>::cast(script));
1143}
1144
1145
1146static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1147 NoHandleAllocation ha;
1148 ASSERT(args.length() == 1);
1149
1150 CONVERT_CHECKED(JSFunction, f, args[0]);
1151 return f->shared()->GetSourceCode();
1152}
1153
1154
1155static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1156 NoHandleAllocation ha;
1157 ASSERT(args.length() == 1);
1158
1159 CONVERT_CHECKED(JSFunction, fun, args[0]);
1160 int pos = fun->shared()->start_position();
1161 return Smi::FromInt(pos);
1162}
1163
1164
1165static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1166 NoHandleAllocation ha;
1167 ASSERT(args.length() == 2);
1168
1169 CONVERT_CHECKED(JSFunction, fun, args[0]);
1170 CONVERT_CHECKED(String, name, args[1]);
1171 fun->SetInstanceClassName(name);
1172 return Heap::undefined_value();
1173}
1174
1175
1176static Object* Runtime_FunctionSetLength(Arguments args) {
1177 NoHandleAllocation ha;
1178 ASSERT(args.length() == 2);
1179
1180 CONVERT_CHECKED(JSFunction, fun, args[0]);
1181 CONVERT_CHECKED(Smi, length, args[1]);
1182 fun->shared()->set_length(length->value());
1183 return length;
1184}
1185
1186
1187static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001188 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001189 ASSERT(args.length() == 2);
1190
1191 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001192 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1193 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001194 return args[0]; // return TOS
1195}
1196
1197
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001198static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1199 NoHandleAllocation ha;
1200 ASSERT(args.length() == 1);
1201
1202 CONVERT_CHECKED(JSFunction, f, args[0]);
1203 // The function_data field of the shared function info is used exclusively by
1204 // the API.
1205 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1206 : Heap::false_value();
1207}
1208
1209
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001210static Object* Runtime_SetCode(Arguments args) {
1211 HandleScope scope;
1212 ASSERT(args.length() == 2);
1213
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001214 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001215 Handle<Object> code = args.at<Object>(1);
1216
1217 Handle<Context> context(target->context());
1218
1219 if (!code->IsNull()) {
1220 RUNTIME_ASSERT(code->IsJSFunction());
1221 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1222 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1223 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1224 return Failure::Exception();
1225 }
1226 // Set the code, formal parameter count, and the length of the target
1227 // function.
1228 target->set_code(fun->code());
1229 target->shared()->set_length(fun->shared()->length());
1230 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001231 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001232 // Set the source code of the target function to undefined.
1233 // SetCode is only used for built-in constructors like String,
1234 // Array, and Object, and some web code
1235 // doesn't like seeing source code for constructors.
1236 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001237 context = Handle<Context>(fun->context());
1238
1239 // Make sure we get a fresh copy of the literal vector to avoid
1240 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001241 int number_of_literals = fun->NumberOfLiterals();
1242 Handle<FixedArray> literals =
1243 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001245 // Insert the object, regexp and array functions in the literals
1246 // array prefix. These are the functions that will be used when
1247 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001248 literals->set(JSFunction::kLiteralGlobalContextIndex,
1249 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001250 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001251 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001252 }
1253
1254 target->set_context(*context);
1255 return *target;
1256}
1257
1258
1259static Object* CharCodeAt(String* subject, Object* index) {
1260 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001261 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001262 // Flatten the string. If someone wants to get a char at an index
1263 // in a cons string, it is likely that more indices will be
1264 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001265 subject->TryFlattenIfNotFlat();
1266 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001267 return Heap::nan_value();
1268 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001269 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001270}
1271
1272
1273static Object* Runtime_StringCharCodeAt(Arguments args) {
1274 NoHandleAllocation ha;
1275 ASSERT(args.length() == 2);
1276
1277 CONVERT_CHECKED(String, subject, args[0]);
1278 Object* index = args[1];
1279 return CharCodeAt(subject, index);
1280}
1281
1282
1283static Object* Runtime_CharFromCode(Arguments args) {
1284 NoHandleAllocation ha;
1285 ASSERT(args.length() == 1);
1286 uint32_t code;
1287 if (Array::IndexFromObject(args[0], &code)) {
1288 if (code <= 0xffff) {
1289 return Heap::LookupSingleCharacterStringFromCode(code);
1290 }
1291 }
1292 return Heap::empty_string();
1293}
1294
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001295// Forward declarations.
1296static const int kStringBuilderConcatHelperLengthBits = 11;
1297static const int kStringBuilderConcatHelperPositionBits = 19;
1298
1299template <typename schar>
1300static inline void StringBuilderConcatHelper(String*,
1301 schar*,
1302 FixedArray*,
1303 int);
1304
1305typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1306typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1307
1308class ReplacementStringBuilder {
1309 public:
1310 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1311 : subject_(subject),
1312 parts_(Factory::NewFixedArray(estimated_part_count)),
1313 part_count_(0),
1314 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001315 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001316 // Require a non-zero initial size. Ensures that doubling the size to
1317 // extend the array will work.
1318 ASSERT(estimated_part_count > 0);
1319 }
1320
1321 void EnsureCapacity(int elements) {
1322 int length = parts_->length();
1323 int required_length = part_count_ + elements;
1324 if (length < required_length) {
1325 int new_length = length;
1326 do {
1327 new_length *= 2;
1328 } while (new_length < required_length);
1329 Handle<FixedArray> extended_array =
1330 Factory::NewFixedArray(new_length);
1331 parts_->CopyTo(0, *extended_array, 0, part_count_);
1332 parts_ = extended_array;
1333 }
1334 }
1335
1336 void AddSubjectSlice(int from, int to) {
1337 ASSERT(from >= 0);
1338 int length = to - from;
1339 ASSERT(length > 0);
1340 // Can we encode the slice in 11 bits for length and 19 bits for
1341 // start position - as used by StringBuilderConcatHelper?
1342 if (StringBuilderSubstringLength::is_valid(length) &&
1343 StringBuilderSubstringPosition::is_valid(from)) {
1344 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1345 StringBuilderSubstringPosition::encode(from);
1346 AddElement(Smi::FromInt(encoded_slice));
1347 } else {
1348 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1349 AddElement(*slice);
1350 }
1351 IncrementCharacterCount(length);
1352 }
1353
1354
1355 void AddString(Handle<String> string) {
1356 int length = string->length();
1357 ASSERT(length > 0);
1358 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001359 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001360 is_ascii_ = false;
1361 }
1362 IncrementCharacterCount(length);
1363 }
1364
1365
1366 Handle<String> ToString() {
1367 if (part_count_ == 0) {
1368 return Factory::empty_string();
1369 }
1370
1371 Handle<String> joined_string;
1372 if (is_ascii_) {
1373 joined_string = NewRawAsciiString(character_count_);
1374 AssertNoAllocation no_alloc;
1375 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1376 char* char_buffer = seq->GetChars();
1377 StringBuilderConcatHelper(*subject_,
1378 char_buffer,
1379 *parts_,
1380 part_count_);
1381 } else {
1382 // Non-ASCII.
1383 joined_string = NewRawTwoByteString(character_count_);
1384 AssertNoAllocation no_alloc;
1385 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1386 uc16* char_buffer = seq->GetChars();
1387 StringBuilderConcatHelper(*subject_,
1388 char_buffer,
1389 *parts_,
1390 part_count_);
1391 }
1392 return joined_string;
1393 }
1394
1395
1396 void IncrementCharacterCount(int by) {
1397 if (character_count_ > Smi::kMaxValue - by) {
1398 V8::FatalProcessOutOfMemory("String.replace result too large.");
1399 }
1400 character_count_ += by;
1401 }
1402
1403 private:
1404
1405 Handle<String> NewRawAsciiString(int size) {
1406 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1407 }
1408
1409
1410 Handle<String> NewRawTwoByteString(int size) {
1411 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1412 }
1413
1414
1415 void AddElement(Object* element) {
1416 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001417 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001418 parts_->set(part_count_, element);
1419 part_count_++;
1420 }
1421
1422 Handle<String> subject_;
1423 Handle<FixedArray> parts_;
1424 int part_count_;
1425 int character_count_;
1426 bool is_ascii_;
1427};
1428
1429
1430class CompiledReplacement {
1431 public:
1432 CompiledReplacement()
1433 : parts_(1), replacement_substrings_(0) {}
1434
1435 void Compile(Handle<String> replacement,
1436 int capture_count,
1437 int subject_length);
1438
1439 void Apply(ReplacementStringBuilder* builder,
1440 int match_from,
1441 int match_to,
1442 Handle<JSArray> last_match_info);
1443
1444 // Number of distinct parts of the replacement pattern.
1445 int parts() {
1446 return parts_.length();
1447 }
1448 private:
1449 enum PartType {
1450 SUBJECT_PREFIX = 1,
1451 SUBJECT_SUFFIX,
1452 SUBJECT_CAPTURE,
1453 REPLACEMENT_SUBSTRING,
1454 REPLACEMENT_STRING,
1455
1456 NUMBER_OF_PART_TYPES
1457 };
1458
1459 struct ReplacementPart {
1460 static inline ReplacementPart SubjectMatch() {
1461 return ReplacementPart(SUBJECT_CAPTURE, 0);
1462 }
1463 static inline ReplacementPart SubjectCapture(int capture_index) {
1464 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1465 }
1466 static inline ReplacementPart SubjectPrefix() {
1467 return ReplacementPart(SUBJECT_PREFIX, 0);
1468 }
1469 static inline ReplacementPart SubjectSuffix(int subject_length) {
1470 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1471 }
1472 static inline ReplacementPart ReplacementString() {
1473 return ReplacementPart(REPLACEMENT_STRING, 0);
1474 }
1475 static inline ReplacementPart ReplacementSubString(int from, int to) {
1476 ASSERT(from >= 0);
1477 ASSERT(to > from);
1478 return ReplacementPart(-from, to);
1479 }
1480
1481 // If tag <= 0 then it is the negation of a start index of a substring of
1482 // the replacement pattern, otherwise it's a value from PartType.
1483 ReplacementPart(int tag, int data)
1484 : tag(tag), data(data) {
1485 // Must be non-positive or a PartType value.
1486 ASSERT(tag < NUMBER_OF_PART_TYPES);
1487 }
1488 // Either a value of PartType or a non-positive number that is
1489 // the negation of an index into the replacement string.
1490 int tag;
1491 // The data value's interpretation depends on the value of tag:
1492 // tag == SUBJECT_PREFIX ||
1493 // tag == SUBJECT_SUFFIX: data is unused.
1494 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1495 // tag == REPLACEMENT_SUBSTRING ||
1496 // tag == REPLACEMENT_STRING: data is index into array of substrings
1497 // of the replacement string.
1498 // tag <= 0: Temporary representation of the substring of the replacement
1499 // string ranging over -tag .. data.
1500 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1501 // substring objects.
1502 int data;
1503 };
1504
1505 template<typename Char>
1506 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1507 Vector<Char> characters,
1508 int capture_count,
1509 int subject_length) {
1510 int length = characters.length();
1511 int last = 0;
1512 for (int i = 0; i < length; i++) {
1513 Char c = characters[i];
1514 if (c == '$') {
1515 int next_index = i + 1;
1516 if (next_index == length) { // No next character!
1517 break;
1518 }
1519 Char c2 = characters[next_index];
1520 switch (c2) {
1521 case '$':
1522 if (i > last) {
1523 // There is a substring before. Include the first "$".
1524 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1525 last = next_index + 1; // Continue after the second "$".
1526 } else {
1527 // Let the next substring start with the second "$".
1528 last = next_index;
1529 }
1530 i = next_index;
1531 break;
1532 case '`':
1533 if (i > last) {
1534 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1535 }
1536 parts->Add(ReplacementPart::SubjectPrefix());
1537 i = next_index;
1538 last = i + 1;
1539 break;
1540 case '\'':
1541 if (i > last) {
1542 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1543 }
1544 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1545 i = next_index;
1546 last = i + 1;
1547 break;
1548 case '&':
1549 if (i > last) {
1550 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1551 }
1552 parts->Add(ReplacementPart::SubjectMatch());
1553 i = next_index;
1554 last = i + 1;
1555 break;
1556 case '0':
1557 case '1':
1558 case '2':
1559 case '3':
1560 case '4':
1561 case '5':
1562 case '6':
1563 case '7':
1564 case '8':
1565 case '9': {
1566 int capture_ref = c2 - '0';
1567 if (capture_ref > capture_count) {
1568 i = next_index;
1569 continue;
1570 }
1571 int second_digit_index = next_index + 1;
1572 if (second_digit_index < length) {
1573 // Peek ahead to see if we have two digits.
1574 Char c3 = characters[second_digit_index];
1575 if ('0' <= c3 && c3 <= '9') { // Double digits.
1576 int double_digit_ref = capture_ref * 10 + c3 - '0';
1577 if (double_digit_ref <= capture_count) {
1578 next_index = second_digit_index;
1579 capture_ref = double_digit_ref;
1580 }
1581 }
1582 }
1583 if (capture_ref > 0) {
1584 if (i > last) {
1585 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1586 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001587 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001588 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1589 last = next_index + 1;
1590 }
1591 i = next_index;
1592 break;
1593 }
1594 default:
1595 i = next_index;
1596 break;
1597 }
1598 }
1599 }
1600 if (length > last) {
1601 if (last == 0) {
1602 parts->Add(ReplacementPart::ReplacementString());
1603 } else {
1604 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1605 }
1606 }
1607 }
1608
1609 ZoneList<ReplacementPart> parts_;
1610 ZoneList<Handle<String> > replacement_substrings_;
1611};
1612
1613
1614void CompiledReplacement::Compile(Handle<String> replacement,
1615 int capture_count,
1616 int subject_length) {
1617 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001618 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001619 AssertNoAllocation no_alloc;
1620 ParseReplacementPattern(&parts_,
1621 replacement->ToAsciiVector(),
1622 capture_count,
1623 subject_length);
1624 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001625 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001626 AssertNoAllocation no_alloc;
1627
1628 ParseReplacementPattern(&parts_,
1629 replacement->ToUC16Vector(),
1630 capture_count,
1631 subject_length);
1632 }
1633 // Find substrings of replacement string and create them as String objects..
1634 int substring_index = 0;
1635 for (int i = 0, n = parts_.length(); i < n; i++) {
1636 int tag = parts_[i].tag;
1637 if (tag <= 0) { // A replacement string slice.
1638 int from = -tag;
1639 int to = parts_[i].data;
1640 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1641 from,
1642 to));
1643 parts_[i].tag = REPLACEMENT_SUBSTRING;
1644 parts_[i].data = substring_index;
1645 substring_index++;
1646 } else if (tag == REPLACEMENT_STRING) {
1647 replacement_substrings_.Add(replacement);
1648 parts_[i].data = substring_index;
1649 substring_index++;
1650 }
1651 }
1652}
1653
1654
1655void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1656 int match_from,
1657 int match_to,
1658 Handle<JSArray> last_match_info) {
1659 for (int i = 0, n = parts_.length(); i < n; i++) {
1660 ReplacementPart part = parts_[i];
1661 switch (part.tag) {
1662 case SUBJECT_PREFIX:
1663 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1664 break;
1665 case SUBJECT_SUFFIX: {
1666 int subject_length = part.data;
1667 if (match_to < subject_length) {
1668 builder->AddSubjectSlice(match_to, subject_length);
1669 }
1670 break;
1671 }
1672 case SUBJECT_CAPTURE: {
1673 int capture = part.data;
1674 FixedArray* match_info = last_match_info->elements();
1675 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1676 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1677 if (from >= 0 && to > from) {
1678 builder->AddSubjectSlice(from, to);
1679 }
1680 break;
1681 }
1682 case REPLACEMENT_SUBSTRING:
1683 case REPLACEMENT_STRING:
1684 builder->AddString(replacement_substrings_[part.data]);
1685 break;
1686 default:
1687 UNREACHABLE();
1688 }
1689 }
1690}
1691
1692
1693
1694static Object* StringReplaceRegExpWithString(String* subject,
1695 JSRegExp* regexp,
1696 String* replacement,
1697 JSArray* last_match_info) {
1698 ASSERT(subject->IsFlat());
1699 ASSERT(replacement->IsFlat());
1700
1701 HandleScope handles;
1702
1703 int length = subject->length();
1704 Handle<String> subject_handle(subject);
1705 Handle<JSRegExp> regexp_handle(regexp);
1706 Handle<String> replacement_handle(replacement);
1707 Handle<JSArray> last_match_info_handle(last_match_info);
1708 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1709 subject_handle,
1710 0,
1711 last_match_info_handle);
1712 if (match.is_null()) {
1713 return Failure::Exception();
1714 }
1715 if (match->IsNull()) {
1716 return *subject_handle;
1717 }
1718
1719 int capture_count = regexp_handle->CaptureCount();
1720
1721 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001722 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001723 CompiledReplacement compiled_replacement;
1724 compiled_replacement.Compile(replacement_handle,
1725 capture_count,
1726 length);
1727
1728 bool is_global = regexp_handle->GetFlags().is_global();
1729
1730 // Guessing the number of parts that the final result string is built
1731 // from. Global regexps can match any number of times, so we guess
1732 // conservatively.
1733 int expected_parts =
1734 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1735 ReplacementStringBuilder builder(subject_handle, expected_parts);
1736
1737 // Index of end of last match.
1738 int prev = 0;
1739
1740 // Number of parts added by compiled replacement plus preceeding string
1741 // and possibly suffix after last match.
1742 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1743 bool matched = true;
1744 do {
1745 ASSERT(last_match_info_handle->HasFastElements());
1746 // Increase the capacity of the builder before entering local handle-scope,
1747 // so its internal buffer can safely allocate a new handle if it grows.
1748 builder.EnsureCapacity(parts_added_per_loop);
1749
1750 HandleScope loop_scope;
1751 int start, end;
1752 {
1753 AssertNoAllocation match_info_array_is_not_in_a_handle;
1754 FixedArray* match_info_array = last_match_info_handle->elements();
1755
1756 ASSERT_EQ(capture_count * 2 + 2,
1757 RegExpImpl::GetLastCaptureCount(match_info_array));
1758 start = RegExpImpl::GetCapture(match_info_array, 0);
1759 end = RegExpImpl::GetCapture(match_info_array, 1);
1760 }
1761
1762 if (prev < start) {
1763 builder.AddSubjectSlice(prev, start);
1764 }
1765 compiled_replacement.Apply(&builder,
1766 start,
1767 end,
1768 last_match_info_handle);
1769 prev = end;
1770
1771 // Only continue checking for global regexps.
1772 if (!is_global) break;
1773
1774 // Continue from where the match ended, unless it was an empty match.
1775 int next = end;
1776 if (start == end) {
1777 next = end + 1;
1778 if (next > length) break;
1779 }
1780
1781 match = RegExpImpl::Exec(regexp_handle,
1782 subject_handle,
1783 next,
1784 last_match_info_handle);
1785 if (match.is_null()) {
1786 return Failure::Exception();
1787 }
1788 matched = !match->IsNull();
1789 } while (matched);
1790
1791 if (prev < length) {
1792 builder.AddSubjectSlice(prev, length);
1793 }
1794
1795 return *(builder.ToString());
1796}
1797
1798
1799static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1800 ASSERT(args.length() == 4);
1801
1802 CONVERT_CHECKED(String, subject, args[0]);
1803 if (!subject->IsFlat()) {
1804 Object* flat_subject = subject->TryFlatten();
1805 if (flat_subject->IsFailure()) {
1806 return flat_subject;
1807 }
1808 subject = String::cast(flat_subject);
1809 }
1810
1811 CONVERT_CHECKED(String, replacement, args[2]);
1812 if (!replacement->IsFlat()) {
1813 Object* flat_replacement = replacement->TryFlatten();
1814 if (flat_replacement->IsFailure()) {
1815 return flat_replacement;
1816 }
1817 replacement = String::cast(flat_replacement);
1818 }
1819
1820 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1821 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1822
1823 ASSERT(last_match_info->HasFastElements());
1824
1825 return StringReplaceRegExpWithString(subject,
1826 regexp,
1827 replacement,
1828 last_match_info);
1829}
1830
1831
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001832
ager@chromium.org7c537e22008-10-16 08:43:32 +00001833// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1834// limit, we can fix the size of tables.
1835static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001836// Reduce alphabet to this size.
1837static const int kBMAlphabetSize = 0x100;
1838// For patterns below this length, the skip length of Boyer-Moore is too short
1839// to compensate for the algorithmic overhead compared to simple brute force.
1840static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001841
ager@chromium.org7c537e22008-10-16 08:43:32 +00001842// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1843// shift. Only allows the last kBMMaxShift characters of the needle
1844// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001845class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001846 public:
1847 BMGoodSuffixBuffers() {}
1848 inline void init(int needle_length) {
1849 ASSERT(needle_length > 1);
1850 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1851 int len = needle_length - start;
1852 biased_suffixes_ = suffixes_ - start;
1853 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1854 for (int i = 0; i <= len; i++) {
1855 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001856 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001857 }
1858 inline int& suffix(int index) {
1859 ASSERT(biased_suffixes_ + index >= suffixes_);
1860 return biased_suffixes_[index];
1861 }
1862 inline int& shift(int index) {
1863 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1864 return biased_good_suffix_shift_[index];
1865 }
1866 private:
1867 int suffixes_[kBMMaxShift + 1];
1868 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001869 int* biased_suffixes_;
1870 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001871 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1872};
1873
1874// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001875static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001876static BMGoodSuffixBuffers bmgs_buffers;
1877
1878// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001879template <typename pchar>
1880static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1881 int start) {
1882 // Run forwards to populate bad_char_table, so that *last* instance
1883 // of character equivalence class is the one registered.
1884 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001885 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1886 : kBMAlphabetSize;
1887 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001888 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001889 } else {
1890 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001891 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001892 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001893 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001894 for (int i = start; i < pattern.length() - 1; i++) {
1895 pchar c = pattern[i];
1896 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001897 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001898 }
1899}
1900
1901template <typename pchar>
1902static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001903 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001904 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001905 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001906 // Compute Good Suffix tables.
1907 bmgs_buffers.init(m);
1908
1909 bmgs_buffers.shift(m-1) = 1;
1910 bmgs_buffers.suffix(m) = m + 1;
1911 pchar last_char = pattern[m - 1];
1912 int suffix = m + 1;
1913 for (int i = m; i > start;) {
1914 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1915 if (bmgs_buffers.shift(suffix) == len) {
1916 bmgs_buffers.shift(suffix) = suffix - i;
1917 }
1918 suffix = bmgs_buffers.suffix(suffix);
1919 }
1920 i--;
1921 suffix--;
1922 bmgs_buffers.suffix(i) = suffix;
1923 if (suffix == m) {
1924 // No suffix to extend, so we check against last_char only.
1925 while (i > start && pattern[i - 1] != last_char) {
1926 if (bmgs_buffers.shift(m) == len) {
1927 bmgs_buffers.shift(m) = m - i;
1928 }
1929 i--;
1930 bmgs_buffers.suffix(i) = m;
1931 }
1932 if (i > start) {
1933 i--;
1934 suffix--;
1935 bmgs_buffers.suffix(i) = suffix;
1936 }
1937 }
1938 }
1939 if (suffix < m) {
1940 for (int i = start; i <= m; i++) {
1941 if (bmgs_buffers.shift(i) == len) {
1942 bmgs_buffers.shift(i) = suffix - start;
1943 }
1944 if (i == suffix) {
1945 suffix = bmgs_buffers.suffix(suffix);
1946 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001947 }
1948 }
1949}
1950
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001951template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001952static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001953 if (sizeof(schar) == 1) {
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 }
1956 if (sizeof(pchar) == 1) {
1957 if (char_code > String::kMaxAsciiCharCode) {
1958 return -1;
1959 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001960 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001961 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001962 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001963}
1964
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001965// Restricted simplified Boyer-Moore string matching.
1966// Uses only the bad-shift table of Boyer-Moore and only uses it
1967// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001968template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001969static int BoyerMooreHorspool(Vector<const schar> subject,
1970 Vector<const pchar> pattern,
1971 int start_index,
1972 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001973 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001974 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001975 // Only preprocess at most kBMMaxShift last characters of pattern.
1976 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001977
ager@chromium.org7c537e22008-10-16 08:43:32 +00001978 BoyerMoorePopulateBadCharTable(pattern, start);
1979
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001980 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001981 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001982 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001983 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001984 // Perform search
1985 for (idx = start_index; idx <= n - m;) {
1986 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001987 int c;
1988 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001989 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001990 int shift = j - bc_occ;
1991 idx += shift;
1992 badness += 1 - shift; // at most zero, so badness cannot increase.
1993 if (idx > n - m) {
1994 *complete = true;
1995 return -1;
1996 }
1997 }
1998 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001999 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002000 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002001 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002002 return idx;
2003 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002004 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002005 // Badness increases by the number of characters we have
2006 // checked, and decreases by the number of characters we
2007 // can skip by shifting. It's a measure of how we are doing
2008 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002009 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002010 if (badness > 0) {
2011 *complete = false;
2012 return idx;
2013 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002014 }
2015 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002016 *complete = true;
2017 return -1;
2018}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002019
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002020
2021template <typename schar, typename pchar>
2022static int BoyerMooreIndexOf(Vector<const schar> subject,
2023 Vector<const pchar> pattern,
2024 int idx) {
2025 int n = subject.length();
2026 int m = pattern.length();
2027 // Only preprocess at most kBMMaxShift last characters of pattern.
2028 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2029
2030 // Build the Good Suffix table and continue searching.
2031 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2032 pchar last_char = pattern[m - 1];
2033 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002034 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002035 int j = m - 1;
2036 schar c;
2037 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002038 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002039 idx += shift;
2040 if (idx > n - m) {
2041 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002042 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002043 }
2044 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2045 if (j < 0) {
2046 return idx;
2047 } else if (j < start) {
2048 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002049 // Fall back on BMH shift.
2050 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002051 } else {
2052 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002053 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002054 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002055 if (gs_shift > shift) {
2056 shift = gs_shift;
2057 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002058 idx += shift;
2059 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002060 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002061
2062 return -1;
2063}
2064
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002065
2066template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002067static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002068 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002069 int start_index) {
2070 for (int i = start_index, n = string.length(); i < n; i++) {
2071 if (pattern_char == string[i]) {
2072 return i;
2073 }
2074 }
2075 return -1;
2076}
2077
2078// Trivial string search for shorter strings.
2079// On return, if "complete" is set to true, the return value is the
2080// final result of searching for the patter in the subject.
2081// If "complete" is set to false, the return value is the index where
2082// further checking should start, i.e., it's guaranteed that the pattern
2083// does not occur at a position prior to the returned index.
2084template <typename pchar, typename schar>
2085static int SimpleIndexOf(Vector<const schar> subject,
2086 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002087 int idx,
2088 bool* complete) {
2089 // Badness is a count of how much work we have done. When we have
2090 // done enough work we decide it's probably worth switching to a better
2091 // algorithm.
2092 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002093 // We know our pattern is at least 2 characters, we cache the first so
2094 // the common case of the first character not matching is faster.
2095 pchar pattern_first_char = pattern[0];
2096
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002097 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2098 badness++;
2099 if (badness > 0) {
2100 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002101 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002102 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002103 if (subject[i] != pattern_first_char) continue;
2104 int j = 1;
2105 do {
2106 if (pattern[j] != subject[i+j]) {
2107 break;
2108 }
2109 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002110 } while (j < pattern.length());
2111 if (j == pattern.length()) {
2112 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002113 return i;
2114 }
2115 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002116 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002117 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002118 return -1;
2119}
2120
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002121// Simple indexOf that never bails out. For short patterns only.
2122template <typename pchar, typename schar>
2123static int SimpleIndexOf(Vector<const schar> subject,
2124 Vector<const pchar> pattern,
2125 int idx) {
2126 pchar pattern_first_char = pattern[0];
2127 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2128 if (subject[i] != pattern_first_char) continue;
2129 int j = 1;
2130 do {
2131 if (pattern[j] != subject[i+j]) {
2132 break;
2133 }
2134 j++;
2135 } while (j < pattern.length());
2136 if (j == pattern.length()) {
2137 return i;
2138 }
2139 }
2140 return -1;
2141}
2142
2143
2144// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002145template <typename schar, typename pchar>
2146static int StringMatchStrategy(Vector<const schar> sub,
2147 Vector<const pchar> pat,
2148 int start_index) {
2149 ASSERT(pat.length() > 1);
2150
2151 // We have an ASCII haystack and a non-ASCII needle. Check if there
2152 // really is a non-ASCII character in the needle and bail out if there
2153 // is.
2154 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2155 for (int i = 0; i < pat.length(); i++) {
2156 uc16 c = pat[i];
2157 if (c > String::kMaxAsciiCharCode) {
2158 return -1;
2159 }
2160 }
2161 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002162 if (pat.length() < kBMMinPatternLength) {
2163 // We don't believe fancy searching can ever be more efficient.
2164 // The max shift of Boyer-Moore on a pattern of this length does
2165 // not compensate for the overhead.
2166 return SimpleIndexOf(sub, pat, start_index);
2167 }
2168 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002169 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002170 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2171 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002172 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002173 if (complete) return idx;
2174 return BoyerMooreIndexOf(sub, pat, idx);
2175}
2176
2177// Perform string match of pattern on subject, starting at start index.
2178// Caller must ensure that 0 <= start_index <= sub->length(),
2179// and should check that pat->length() + start_index <= sub->length()
2180int Runtime::StringMatch(Handle<String> sub,
2181 Handle<String> pat,
2182 int start_index) {
2183 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002184 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002185
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002186 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002187 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002188
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002189 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002190 if (start_index + pattern_length > subject_length) return -1;
2191
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002192 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002193 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002194 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002195 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002196 // character patterns linear search is necessary, so any smart
2197 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002198 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002199 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002200 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002201 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002202 if (pchar > String::kMaxAsciiCharCode) {
2203 return -1;
2204 }
2205 Vector<const char> ascii_vector =
2206 sub->ToAsciiVector().SubVector(start_index, subject_length);
2207 const void* pos = memchr(ascii_vector.start(),
2208 static_cast<const char>(pchar),
2209 static_cast<size_t>(ascii_vector.length()));
2210 if (pos == NULL) {
2211 return -1;
2212 }
2213 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2214 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002215 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002216 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002217 }
2218
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002219 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002220 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002221 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002222
ager@chromium.org7c537e22008-10-16 08:43:32 +00002223 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2224 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002225 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002226 Vector<const char> pat_vector = pat->ToAsciiVector();
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);
ager@chromium.org236ad962008-09-25 09:45:57 +00002229 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002230 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002231 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002232 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002233 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002234 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002235 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002236 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002237}
2238
2239
2240static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002241 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002242 ASSERT(args.length() == 3);
2243
ager@chromium.org7c537e22008-10-16 08:43:32 +00002244 CONVERT_ARG_CHECKED(String, sub, 0);
2245 CONVERT_ARG_CHECKED(String, pat, 1);
2246
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002247 Object* index = args[2];
2248 uint32_t start_index;
2249 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2250
ager@chromium.org870a0b62008-11-04 11:43:05 +00002251 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002252 int position = Runtime::StringMatch(sub, pat, start_index);
2253 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002254}
2255
2256
2257static Object* Runtime_StringLastIndexOf(Arguments args) {
2258 NoHandleAllocation ha;
2259 ASSERT(args.length() == 3);
2260
2261 CONVERT_CHECKED(String, sub, args[0]);
2262 CONVERT_CHECKED(String, pat, args[1]);
2263 Object* index = args[2];
2264
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002265 sub->TryFlattenIfNotFlat();
2266 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002267
2268 uint32_t start_index;
2269 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2270
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002271 uint32_t pattern_length = pat->length();
2272 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002273
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002274 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002275 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002276 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002277
2278 for (int i = start_index; i >= 0; i--) {
2279 bool found = true;
2280 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002281 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002282 found = false;
2283 break;
2284 }
2285 }
2286 if (found) return Smi::FromInt(i);
2287 }
2288
2289 return Smi::FromInt(-1);
2290}
2291
2292
2293static Object* Runtime_StringLocaleCompare(Arguments args) {
2294 NoHandleAllocation ha;
2295 ASSERT(args.length() == 2);
2296
2297 CONVERT_CHECKED(String, str1, args[0]);
2298 CONVERT_CHECKED(String, str2, args[1]);
2299
2300 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002301 int str1_length = str1->length();
2302 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002303
2304 // Decide trivial cases without flattening.
2305 if (str1_length == 0) {
2306 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2307 return Smi::FromInt(-str2_length);
2308 } else {
2309 if (str2_length == 0) return Smi::FromInt(str1_length);
2310 }
2311
2312 int end = str1_length < str2_length ? str1_length : str2_length;
2313
2314 // No need to flatten if we are going to find the answer on the first
2315 // character. At this point we know there is at least one character
2316 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002317 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002318 if (d != 0) return Smi::FromInt(d);
2319
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002320 str1->TryFlattenIfNotFlat();
2321 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002322
2323 static StringInputBuffer buf1;
2324 static StringInputBuffer buf2;
2325
2326 buf1.Reset(str1);
2327 buf2.Reset(str2);
2328
2329 for (int i = 0; i < end; i++) {
2330 uint16_t char1 = buf1.GetNext();
2331 uint16_t char2 = buf2.GetNext();
2332 if (char1 != char2) return Smi::FromInt(char1 - char2);
2333 }
2334
2335 return Smi::FromInt(str1_length - str2_length);
2336}
2337
2338
2339static Object* Runtime_StringSlice(Arguments args) {
2340 NoHandleAllocation ha;
2341 ASSERT(args.length() == 3);
2342
2343 CONVERT_CHECKED(String, value, args[0]);
2344 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2345 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2346
2347 int start = FastD2I(from_number);
2348 int end = FastD2I(to_number);
2349
2350 RUNTIME_ASSERT(end >= start);
2351 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002352 RUNTIME_ASSERT(end <= value->length());
2353 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002354}
2355
2356
ager@chromium.org41826e72009-03-30 13:30:57 +00002357static Object* Runtime_StringMatch(Arguments args) {
2358 ASSERT_EQ(3, args.length());
2359
2360 CONVERT_ARG_CHECKED(String, subject, 0);
2361 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2362 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2363 HandleScope handles;
2364
2365 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2366
2367 if (match.is_null()) {
2368 return Failure::Exception();
2369 }
2370 if (match->IsNull()) {
2371 return Heap::null_value();
2372 }
2373 int length = subject->length();
2374
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002375 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002376 ZoneList<int> offsets(8);
2377 do {
2378 int start;
2379 int end;
2380 {
2381 AssertNoAllocation no_alloc;
2382 FixedArray* elements = regexp_info->elements();
2383 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2384 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2385 }
2386 offsets.Add(start);
2387 offsets.Add(end);
2388 int index = start < end ? end : end + 1;
2389 if (index > length) break;
2390 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2391 if (match.is_null()) {
2392 return Failure::Exception();
2393 }
2394 } while (!match->IsNull());
2395 int matches = offsets.length() / 2;
2396 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2397 for (int i = 0; i < matches ; i++) {
2398 int from = offsets.at(i * 2);
2399 int to = offsets.at(i * 2 + 1);
2400 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2401 }
2402 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2403 result->set_length(Smi::FromInt(matches));
2404 return *result;
2405}
2406
2407
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002408static Object* Runtime_NumberToRadixString(Arguments args) {
2409 NoHandleAllocation ha;
2410 ASSERT(args.length() == 2);
2411
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002412 // Fast case where the result is a one character string.
2413 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2414 int value = Smi::cast(args[0])->value();
2415 int radix = Smi::cast(args[1])->value();
2416 if (value >= 0 && value < radix) {
2417 RUNTIME_ASSERT(radix <= 36);
2418 // Character array used for conversion.
2419 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2420 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2421 }
2422 }
2423
2424 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002425 CONVERT_DOUBLE_CHECKED(value, args[0]);
2426 if (isnan(value)) {
2427 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2428 }
2429 if (isinf(value)) {
2430 if (value < 0) {
2431 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2432 }
2433 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2434 }
2435 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2436 int radix = FastD2I(radix_number);
2437 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2438 char* str = DoubleToRadixCString(value, radix);
2439 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2440 DeleteArray(str);
2441 return result;
2442}
2443
2444
2445static Object* Runtime_NumberToFixed(Arguments args) {
2446 NoHandleAllocation ha;
2447 ASSERT(args.length() == 2);
2448
2449 CONVERT_DOUBLE_CHECKED(value, args[0]);
2450 if (isnan(value)) {
2451 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2452 }
2453 if (isinf(value)) {
2454 if (value < 0) {
2455 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2456 }
2457 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2458 }
2459 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2460 int f = FastD2I(f_number);
2461 RUNTIME_ASSERT(f >= 0);
2462 char* str = DoubleToFixedCString(value, f);
2463 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2464 DeleteArray(str);
2465 return res;
2466}
2467
2468
2469static Object* Runtime_NumberToExponential(Arguments args) {
2470 NoHandleAllocation ha;
2471 ASSERT(args.length() == 2);
2472
2473 CONVERT_DOUBLE_CHECKED(value, args[0]);
2474 if (isnan(value)) {
2475 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2476 }
2477 if (isinf(value)) {
2478 if (value < 0) {
2479 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2480 }
2481 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2482 }
2483 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2484 int f = FastD2I(f_number);
2485 RUNTIME_ASSERT(f >= -1 && f <= 20);
2486 char* str = DoubleToExponentialCString(value, f);
2487 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2488 DeleteArray(str);
2489 return res;
2490}
2491
2492
2493static Object* Runtime_NumberToPrecision(Arguments args) {
2494 NoHandleAllocation ha;
2495 ASSERT(args.length() == 2);
2496
2497 CONVERT_DOUBLE_CHECKED(value, args[0]);
2498 if (isnan(value)) {
2499 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2500 }
2501 if (isinf(value)) {
2502 if (value < 0) {
2503 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2504 }
2505 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2506 }
2507 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2508 int f = FastD2I(f_number);
2509 RUNTIME_ASSERT(f >= 1 && f <= 21);
2510 char* str = DoubleToPrecisionCString(value, f);
2511 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2512 DeleteArray(str);
2513 return res;
2514}
2515
2516
2517// Returns a single character string where first character equals
2518// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002519static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002520 if (index < static_cast<uint32_t>(string->length())) {
2521 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002522 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002523 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002525 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526}
2527
2528
2529Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2530 // Handle [] indexing on Strings
2531 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002532 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2533 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002534 }
2535
2536 // Handle [] indexing on String objects
2537 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002538 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2539 Handle<Object> result =
2540 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2541 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002542 }
2543
2544 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002545 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002546 return prototype->GetElement(index);
2547 }
2548
2549 return object->GetElement(index);
2550}
2551
2552
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002553Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2554 HandleScope scope;
2555
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002556 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002557 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002558 Handle<Object> error =
2559 Factory::NewTypeError("non_object_property_load",
2560 HandleVector(args, 2));
2561 return Top::Throw(*error);
2562 }
2563
2564 // Check if the given key is an array index.
2565 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002566 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002567 return GetElementOrCharAt(object, index);
2568 }
2569
2570 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002571 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002572 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002573 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002574 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002575 bool has_pending_exception = false;
2576 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002577 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002578 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002579 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002580 }
2581
ager@chromium.org32912102009-01-16 10:38:43 +00002582 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002583 // the element if so.
2584 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002585 return GetElementOrCharAt(object, index);
2586 } else {
2587 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002588 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002589 }
2590}
2591
2592
2593static Object* Runtime_GetProperty(Arguments args) {
2594 NoHandleAllocation ha;
2595 ASSERT(args.length() == 2);
2596
2597 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002598 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002599
2600 return Runtime::GetObjectProperty(object, key);
2601}
2602
2603
ager@chromium.org7c537e22008-10-16 08:43:32 +00002604
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002605// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002606static Object* Runtime_KeyedGetProperty(Arguments args) {
2607 NoHandleAllocation ha;
2608 ASSERT(args.length() == 2);
2609
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002610 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002611 // itself.
2612 //
2613 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002614 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002615 // global proxy object never has properties. This is the case
2616 // because the global proxy object forwards everything to its hidden
2617 // prototype including local lookups.
2618 //
2619 // Additionally, we need to make sure that we do not cache results
2620 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002621 if (args[0]->IsJSObject() &&
2622 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002623 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002624 args[1]->IsString()) {
2625 JSObject* receiver = JSObject::cast(args[0]);
2626 String* key = String::cast(args[1]);
2627 if (receiver->HasFastProperties()) {
2628 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002629 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002630 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2631 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002632 Object* value = receiver->FastPropertyAt(offset);
2633 return value->IsTheHole() ? Heap::undefined_value() : value;
2634 }
2635 // Lookup cache miss. Perform lookup and update the cache if
2636 // appropriate.
2637 LookupResult result;
2638 receiver->LocalLookup(key, &result);
2639 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2640 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002641 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002642 Object* value = receiver->FastPropertyAt(offset);
2643 return value->IsTheHole() ? Heap::undefined_value() : value;
2644 }
2645 } else {
2646 // Attempt dictionary lookup.
2647 Dictionary* dictionary = receiver->property_dictionary();
2648 int entry = dictionary->FindStringEntry(key);
2649 if ((entry != DescriptorArray::kNotFound) &&
2650 (dictionary->DetailsAt(entry).type() == NORMAL)) {
2651 return dictionary->ValueAt(entry);
2652 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002653 }
2654 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002655
2656 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002657 return Runtime::GetObjectProperty(args.at<Object>(0),
2658 args.at<Object>(1));
2659}
2660
2661
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002662Object* Runtime::SetObjectProperty(Handle<Object> object,
2663 Handle<Object> key,
2664 Handle<Object> value,
2665 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002666 HandleScope scope;
2667
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002668 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002669 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002670 Handle<Object> error =
2671 Factory::NewTypeError("non_object_property_store",
2672 HandleVector(args, 2));
2673 return Top::Throw(*error);
2674 }
2675
2676 // If the object isn't a JavaScript object, we ignore the store.
2677 if (!object->IsJSObject()) return *value;
2678
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002679 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2680
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002681 // Check if the given key is an array index.
2682 uint32_t index;
2683 if (Array::IndexFromObject(*key, &index)) {
2684 ASSERT(attr == NONE);
2685
2686 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2687 // of a string using [] notation. We need to support this too in
2688 // JavaScript.
2689 // In the case of a String object we just need to redirect the assignment to
2690 // the underlying string if the index is in range. Since the underlying
2691 // string does nothing with the assignment then we can ignore such
2692 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002693 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002694 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002695 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002696
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002697 Handle<Object> result = SetElement(js_object, index, value);
2698 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002699 return *value;
2700 }
2701
2702 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002703 Handle<Object> result;
2704 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002705 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002706 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002707 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002708 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002709 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002710 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002711 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002712 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002713 return *value;
2714 }
2715
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002716 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002717 bool has_pending_exception = false;
2718 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2719 if (has_pending_exception) return Failure::Exception();
2720 Handle<String> name = Handle<String>::cast(converted);
2721
2722 if (name->AsArrayIndex(&index)) {
2723 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002724 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002725 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002726 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002727 }
2728}
2729
2730
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002731Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2732 Handle<Object> key,
2733 Handle<Object> value,
2734 PropertyAttributes attr) {
2735 HandleScope scope;
2736
2737 // Check if the given key is an array index.
2738 uint32_t index;
2739 if (Array::IndexFromObject(*key, &index)) {
2740 ASSERT(attr == NONE);
2741
2742 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2743 // of a string using [] notation. We need to support this too in
2744 // JavaScript.
2745 // In the case of a String object we just need to redirect the assignment to
2746 // the underlying string if the index is in range. Since the underlying
2747 // string does nothing with the assignment then we can ignore such
2748 // assignments.
2749 if (js_object->IsStringObjectWithCharacterAt(index)) {
2750 return *value;
2751 }
2752
2753 return js_object->SetElement(index, *value);
2754 }
2755
2756 if (key->IsString()) {
2757 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2758 ASSERT(attr == NONE);
2759 return js_object->SetElement(index, *value);
2760 } else {
2761 Handle<String> key_string = Handle<String>::cast(key);
2762 key_string->TryFlattenIfNotFlat();
2763 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2764 *value,
2765 attr);
2766 }
2767 }
2768
2769 // Call-back into JavaScript to convert the key to a string.
2770 bool has_pending_exception = false;
2771 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2772 if (has_pending_exception) return Failure::Exception();
2773 Handle<String> name = Handle<String>::cast(converted);
2774
2775 if (name->AsArrayIndex(&index)) {
2776 ASSERT(attr == NONE);
2777 return js_object->SetElement(index, *value);
2778 } else {
2779 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2780 }
2781}
2782
2783
ager@chromium.orge2902be2009-06-08 12:21:35 +00002784Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2785 Handle<Object> key) {
2786 HandleScope scope;
2787
2788 // Check if the given key is an array index.
2789 uint32_t index;
2790 if (Array::IndexFromObject(*key, &index)) {
2791 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2792 // characters of a string using [] notation. In the case of a
2793 // String object we just need to redirect the deletion to the
2794 // underlying string if the index is in range. Since the
2795 // underlying string does nothing with the deletion, we can ignore
2796 // such deletions.
2797 if (js_object->IsStringObjectWithCharacterAt(index)) {
2798 return Heap::true_value();
2799 }
2800
2801 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2802 }
2803
2804 Handle<String> key_string;
2805 if (key->IsString()) {
2806 key_string = Handle<String>::cast(key);
2807 } else {
2808 // Call-back into JavaScript to convert the key to a string.
2809 bool has_pending_exception = false;
2810 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2811 if (has_pending_exception) return Failure::Exception();
2812 key_string = Handle<String>::cast(converted);
2813 }
2814
2815 key_string->TryFlattenIfNotFlat();
2816 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2817}
2818
2819
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002820static Object* Runtime_SetProperty(Arguments args) {
2821 NoHandleAllocation ha;
2822 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2823
2824 Handle<Object> object = args.at<Object>(0);
2825 Handle<Object> key = args.at<Object>(1);
2826 Handle<Object> value = args.at<Object>(2);
2827
2828 // Compute attributes.
2829 PropertyAttributes attributes = NONE;
2830 if (args.length() == 4) {
2831 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002832 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002833 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002834 RUNTIME_ASSERT(
2835 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2836 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002837 }
2838 return Runtime::SetObjectProperty(object, key, value, attributes);
2839}
2840
2841
2842// Set a local property, even if it is READ_ONLY. If the property does not
2843// exist, it will be added with attributes NONE.
2844static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2845 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002846 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002847 CONVERT_CHECKED(JSObject, object, args[0]);
2848 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002849 // Compute attributes.
2850 PropertyAttributes attributes = NONE;
2851 if (args.length() == 4) {
2852 CONVERT_CHECKED(Smi, value_obj, args[3]);
2853 int unchecked_value = value_obj->value();
2854 // Only attribute bits should be set.
2855 RUNTIME_ASSERT(
2856 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2857 attributes = static_cast<PropertyAttributes>(unchecked_value);
2858 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002859
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002860 return object->
2861 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002862}
2863
2864
2865static Object* Runtime_DeleteProperty(Arguments args) {
2866 NoHandleAllocation ha;
2867 ASSERT(args.length() == 2);
2868
2869 CONVERT_CHECKED(JSObject, object, args[0]);
2870 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002871 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002872}
2873
2874
ager@chromium.org9085a012009-05-11 19:22:57 +00002875static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2876 Handle<String> key) {
2877 if (object->HasLocalProperty(*key)) return Heap::true_value();
2878 // Handle hidden prototypes. If there's a hidden prototype above this thing
2879 // then we have to check it for properties, because they are supposed to
2880 // look like they are on this object.
2881 Handle<Object> proto(object->GetPrototype());
2882 if (proto->IsJSObject() &&
2883 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2884 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2885 }
2886 return Heap::false_value();
2887}
2888
2889
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002890static Object* Runtime_HasLocalProperty(Arguments args) {
2891 NoHandleAllocation ha;
2892 ASSERT(args.length() == 2);
2893 CONVERT_CHECKED(String, key, args[1]);
2894
ager@chromium.org9085a012009-05-11 19:22:57 +00002895 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002896 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002897 if (obj->IsJSObject()) {
2898 JSObject* object = JSObject::cast(obj);
2899 // Fast case - no interceptors.
2900 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2901 // Slow case. Either it's not there or we have an interceptor. We should
2902 // have handles for this kind of deal.
2903 HandleScope scope;
2904 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2905 Handle<String>(key));
2906 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907 // Well, there is one exception: Handle [] on strings.
2908 uint32_t index;
2909 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002910 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002911 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002912 return Heap::true_value();
2913 }
2914 }
2915 return Heap::false_value();
2916}
2917
2918
2919static Object* Runtime_HasProperty(Arguments args) {
2920 NoHandleAllocation na;
2921 ASSERT(args.length() == 2);
2922
2923 // Only JS objects can have properties.
2924 if (args[0]->IsJSObject()) {
2925 JSObject* object = JSObject::cast(args[0]);
2926 CONVERT_CHECKED(String, key, args[1]);
2927 if (object->HasProperty(key)) return Heap::true_value();
2928 }
2929 return Heap::false_value();
2930}
2931
2932
2933static Object* Runtime_HasElement(Arguments args) {
2934 NoHandleAllocation na;
2935 ASSERT(args.length() == 2);
2936
2937 // Only JS objects can have elements.
2938 if (args[0]->IsJSObject()) {
2939 JSObject* object = JSObject::cast(args[0]);
2940 CONVERT_CHECKED(Smi, index_obj, args[1]);
2941 uint32_t index = index_obj->value();
2942 if (object->HasElement(index)) return Heap::true_value();
2943 }
2944 return Heap::false_value();
2945}
2946
2947
2948static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2949 NoHandleAllocation ha;
2950 ASSERT(args.length() == 2);
2951
2952 CONVERT_CHECKED(JSObject, object, args[0]);
2953 CONVERT_CHECKED(String, key, args[1]);
2954
2955 uint32_t index;
2956 if (key->AsArrayIndex(&index)) {
2957 return Heap::ToBoolean(object->HasElement(index));
2958 }
2959
ager@chromium.org870a0b62008-11-04 11:43:05 +00002960 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2961 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002962}
2963
2964
2965static Object* Runtime_GetPropertyNames(Arguments args) {
2966 HandleScope scope;
2967 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002968 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002969 return *GetKeysFor(object);
2970}
2971
2972
2973// Returns either a FixedArray as Runtime_GetPropertyNames,
2974// or, if the given object has an enum cache that contains
2975// all enumerable properties of the object and its prototypes
2976// have none, the map of the object. This is used to speed up
2977// the check for deletions during a for-in.
2978static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2979 ASSERT(args.length() == 1);
2980
2981 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2982
2983 if (raw_object->IsSimpleEnum()) return raw_object->map();
2984
2985 HandleScope scope;
2986 Handle<JSObject> object(raw_object);
2987 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2988
2989 // Test again, since cache may have been built by preceding call.
2990 if (object->IsSimpleEnum()) return object->map();
2991
2992 return *content;
2993}
2994
2995
2996static Object* Runtime_GetArgumentsProperty(Arguments args) {
2997 NoHandleAllocation ha;
2998 ASSERT(args.length() == 1);
2999
3000 // Compute the frame holding the arguments.
3001 JavaScriptFrameIterator it;
3002 it.AdvanceToArgumentsFrame();
3003 JavaScriptFrame* frame = it.frame();
3004
3005 // Get the actual number of provided arguments.
3006 const uint32_t n = frame->GetProvidedParametersCount();
3007
3008 // Try to convert the key to an index. If successful and within
3009 // index return the the argument from the frame.
3010 uint32_t index;
3011 if (Array::IndexFromObject(args[0], &index) && index < n) {
3012 return frame->GetParameter(index);
3013 }
3014
3015 // Convert the key to a string.
3016 HandleScope scope;
3017 bool exception = false;
3018 Handle<Object> converted =
3019 Execution::ToString(args.at<Object>(0), &exception);
3020 if (exception) return Failure::Exception();
3021 Handle<String> key = Handle<String>::cast(converted);
3022
3023 // Try to convert the string key into an array index.
3024 if (key->AsArrayIndex(&index)) {
3025 if (index < n) {
3026 return frame->GetParameter(index);
3027 } else {
3028 return Top::initial_object_prototype()->GetElement(index);
3029 }
3030 }
3031
3032 // Handle special arguments properties.
3033 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3034 if (key->Equals(Heap::callee_symbol())) return frame->function();
3035
3036 // Lookup in the initial Object.prototype object.
3037 return Top::initial_object_prototype()->GetProperty(*key);
3038}
3039
3040
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003041static Object* Runtime_ToFastProperties(Arguments args) {
3042 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003043 Handle<Object> object = args.at<Object>(0);
3044 if (object->IsJSObject()) {
3045 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3046 js_object->TransformToFastProperties(0);
3047 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003048 return *object;
3049}
3050
3051
3052static Object* Runtime_ToSlowProperties(Arguments args) {
3053 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003054 Handle<Object> object = args.at<Object>(0);
3055 if (object->IsJSObject()) {
3056 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3057 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
3058 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003059 return *object;
3060}
3061
3062
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003063static Object* Runtime_ToBool(Arguments args) {
3064 NoHandleAllocation ha;
3065 ASSERT(args.length() == 1);
3066
3067 return args[0]->ToBoolean();
3068}
3069
3070
3071// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3072// Possible optimizations: put the type string into the oddballs.
3073static Object* Runtime_Typeof(Arguments args) {
3074 NoHandleAllocation ha;
3075
3076 Object* obj = args[0];
3077 if (obj->IsNumber()) return Heap::number_symbol();
3078 HeapObject* heap_obj = HeapObject::cast(obj);
3079
3080 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003081 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003082
3083 InstanceType instance_type = heap_obj->map()->instance_type();
3084 if (instance_type < FIRST_NONSTRING_TYPE) {
3085 return Heap::string_symbol();
3086 }
3087
3088 switch (instance_type) {
3089 case ODDBALL_TYPE:
3090 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3091 return Heap::boolean_symbol();
3092 }
3093 if (heap_obj->IsNull()) {
3094 return Heap::object_symbol();
3095 }
3096 ASSERT(heap_obj->IsUndefined());
3097 return Heap::undefined_symbol();
3098 case JS_FUNCTION_TYPE:
3099 return Heap::function_symbol();
3100 default:
3101 // For any kind of object not handled above, the spec rule for
3102 // host objects gives that it is okay to return "object"
3103 return Heap::object_symbol();
3104 }
3105}
3106
3107
3108static Object* Runtime_StringToNumber(Arguments args) {
3109 NoHandleAllocation ha;
3110 ASSERT(args.length() == 1);
3111 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003112 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003113 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3114}
3115
3116
3117static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3118 NoHandleAllocation ha;
3119 ASSERT(args.length() == 1);
3120
3121 CONVERT_CHECKED(JSArray, codes, args[0]);
3122 int length = Smi::cast(codes->length())->value();
3123
3124 // Check if the string can be ASCII.
3125 int i;
3126 for (i = 0; i < length; i++) {
3127 Object* element = codes->GetElement(i);
3128 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3129 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3130 break;
3131 }
3132
3133 Object* object = NULL;
3134 if (i == length) { // The string is ASCII.
3135 object = Heap::AllocateRawAsciiString(length);
3136 } else { // The string is not ASCII.
3137 object = Heap::AllocateRawTwoByteString(length);
3138 }
3139
3140 if (object->IsFailure()) return object;
3141 String* result = String::cast(object);
3142 for (int i = 0; i < length; i++) {
3143 Object* element = codes->GetElement(i);
3144 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003145 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003146 }
3147 return result;
3148}
3149
3150
3151// kNotEscaped is generated by the following:
3152//
3153// #!/bin/perl
3154// for (my $i = 0; $i < 256; $i++) {
3155// print "\n" if $i % 16 == 0;
3156// my $c = chr($i);
3157// my $escaped = 1;
3158// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3159// print $escaped ? "0, " : "1, ";
3160// }
3161
3162
3163static bool IsNotEscaped(uint16_t character) {
3164 // Only for 8 bit characters, the rest are always escaped (in a different way)
3165 ASSERT(character < 256);
3166 static const char kNotEscaped[256] = {
3167 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3170 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3171 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3172 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3173 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3174 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3182 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3183 };
3184 return kNotEscaped[character] != 0;
3185}
3186
3187
3188static Object* Runtime_URIEscape(Arguments args) {
3189 const char hex_chars[] = "0123456789ABCDEF";
3190 NoHandleAllocation ha;
3191 ASSERT(args.length() == 1);
3192 CONVERT_CHECKED(String, source, args[0]);
3193
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003194 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003195
3196 int escaped_length = 0;
3197 int length = source->length();
3198 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003199 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003200 buffer->Reset(source);
3201 while (buffer->has_more()) {
3202 uint16_t character = buffer->GetNext();
3203 if (character >= 256) {
3204 escaped_length += 6;
3205 } else if (IsNotEscaped(character)) {
3206 escaped_length++;
3207 } else {
3208 escaped_length += 3;
3209 }
3210 // We don't allow strings that are longer than Smi range.
3211 if (!Smi::IsValid(escaped_length)) {
3212 Top::context()->mark_out_of_memory();
3213 return Failure::OutOfMemoryException();
3214 }
3215 }
3216 }
3217 // No length change implies no change. Return original string if no change.
3218 if (escaped_length == length) {
3219 return source;
3220 }
3221 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3222 if (o->IsFailure()) return o;
3223 String* destination = String::cast(o);
3224 int dest_position = 0;
3225
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003226 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003227 buffer->Rewind();
3228 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003229 uint16_t chr = buffer->GetNext();
3230 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003231 destination->Set(dest_position, '%');
3232 destination->Set(dest_position+1, 'u');
3233 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3234 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3235 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3236 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003237 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003238 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003239 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003240 dest_position++;
3241 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003242 destination->Set(dest_position, '%');
3243 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3244 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003245 dest_position += 3;
3246 }
3247 }
3248 return destination;
3249}
3250
3251
3252static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3253 static const signed char kHexValue['g'] = {
3254 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3255 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3256 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3257 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3258 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3259 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3260 -1, 10, 11, 12, 13, 14, 15 };
3261
3262 if (character1 > 'f') return -1;
3263 int hi = kHexValue[character1];
3264 if (hi == -1) return -1;
3265 if (character2 > 'f') return -1;
3266 int lo = kHexValue[character2];
3267 if (lo == -1) return -1;
3268 return (hi << 4) + lo;
3269}
3270
3271
ager@chromium.org870a0b62008-11-04 11:43:05 +00003272static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003273 int i,
3274 int length,
3275 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003276 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003277 int32_t hi = 0;
3278 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003279 if (character == '%' &&
3280 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003281 source->Get(i + 1) == 'u' &&
3282 (hi = TwoDigitHex(source->Get(i + 2),
3283 source->Get(i + 3))) != -1 &&
3284 (lo = TwoDigitHex(source->Get(i + 4),
3285 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003286 *step = 6;
3287 return (hi << 8) + lo;
3288 } else if (character == '%' &&
3289 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003290 (lo = TwoDigitHex(source->Get(i + 1),
3291 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003292 *step = 3;
3293 return lo;
3294 } else {
3295 *step = 1;
3296 return character;
3297 }
3298}
3299
3300
3301static Object* Runtime_URIUnescape(Arguments args) {
3302 NoHandleAllocation ha;
3303 ASSERT(args.length() == 1);
3304 CONVERT_CHECKED(String, source, args[0]);
3305
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003306 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003307
3308 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003309 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003310
3311 int unescaped_length = 0;
3312 for (int i = 0; i < length; unescaped_length++) {
3313 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003314 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003315 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003316 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003317 i += step;
3318 }
3319
3320 // No length change implies no change. Return original string if no change.
3321 if (unescaped_length == length)
3322 return source;
3323
3324 Object* o = ascii ?
3325 Heap::AllocateRawAsciiString(unescaped_length) :
3326 Heap::AllocateRawTwoByteString(unescaped_length);
3327 if (o->IsFailure()) return o;
3328 String* destination = String::cast(o);
3329
3330 int dest_position = 0;
3331 for (int i = 0; i < length; dest_position++) {
3332 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003333 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003334 i += step;
3335 }
3336 return destination;
3337}
3338
3339
3340static Object* Runtime_StringParseInt(Arguments args) {
3341 NoHandleAllocation ha;
3342
3343 CONVERT_CHECKED(String, s, args[0]);
3344 CONVERT_DOUBLE_CHECKED(n, args[1]);
3345 int radix = FastD2I(n);
3346
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003347 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003348
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003349 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003350 int i;
3351
3352 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003353 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003354 if (i == len) return Heap::nan_value();
3355
3356 // Compute the sign (default to +).
3357 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003358 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003359 sign = -1;
3360 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003361 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003362 i++;
3363 }
3364
3365 // Compute the radix if 0.
3366 if (radix == 0) {
3367 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003368 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003369 radix = 8;
3370 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003371 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003372 if (c == 'x' || c == 'X') {
3373 radix = 16;
3374 i += 2;
3375 }
3376 }
3377 }
3378 } else if (radix == 16) {
3379 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003380 if (i + 1 < len && s->Get(i) == '0') {
3381 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003382 if (c == 'x' || c == 'X') i += 2;
3383 }
3384 }
3385
3386 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3387 double value;
3388 int end_index = StringToInt(s, i, radix, &value);
3389 if (end_index != i) {
3390 return Heap::NumberFromDouble(sign * value);
3391 }
3392 return Heap::nan_value();
3393}
3394
3395
3396static Object* Runtime_StringParseFloat(Arguments args) {
3397 NoHandleAllocation ha;
3398 CONVERT_CHECKED(String, str, args[0]);
3399
3400 // ECMA-262 section 15.1.2.3, empty string is NaN
3401 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3402
3403 // Create a number object from the value.
3404 return Heap::NumberFromDouble(value);
3405}
3406
3407
3408static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3409static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3410
3411
3412template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003413static Object* ConvertCaseHelper(String* s,
3414 int length,
3415 int input_string_length,
3416 unibrow::Mapping<Converter, 128>* mapping) {
3417 // We try this twice, once with the assumption that the result is no longer
3418 // than the input and, if that assumption breaks, again with the exact
3419 // length. This may not be pretty, but it is nicer than what was here before
3420 // and I hereby claim my vaffel-is.
3421 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003422 // Allocate the resulting string.
3423 //
3424 // NOTE: This assumes that the upper/lower case of an ascii
3425 // character is also ascii. This is currently the case, but it
3426 // might break in the future if we implement more context and locale
3427 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003428 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003429 ? Heap::AllocateRawAsciiString(length)
3430 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003431 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003432 String* result = String::cast(o);
3433 bool has_changed_character = false;
3434
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003435 // Convert all characters to upper case, assuming that they will fit
3436 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003437 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003438 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003439 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003440 // We can assume that the string is not empty
3441 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003442 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003443 bool has_next = buffer->has_more();
3444 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003445 int char_length = mapping->get(current, next, chars);
3446 if (char_length == 0) {
3447 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003448 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003449 i++;
3450 } else if (char_length == 1) {
3451 // Common case: converting the letter resulted in one character.
3452 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003453 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003454 has_changed_character = true;
3455 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003456 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003457 // We've assumed that the result would be as long as the
3458 // input but here is a character that converts to several
3459 // characters. No matter, we calculate the exact length
3460 // of the result and try the whole thing again.
3461 //
3462 // Note that this leaves room for optimization. We could just
3463 // memcpy what we already have to the result string. Also,
3464 // the result string is the last object allocated we could
3465 // "realloc" it and probably, in the vast majority of cases,
3466 // extend the existing string to be able to hold the full
3467 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003468 int next_length = 0;
3469 if (has_next) {
3470 next_length = mapping->get(next, 0, chars);
3471 if (next_length == 0) next_length = 1;
3472 }
3473 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003474 while (buffer->has_more()) {
3475 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003476 // NOTE: we use 0 as the next character here because, while
3477 // the next character may affect what a character converts to,
3478 // it does not in any case affect the length of what it convert
3479 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003480 int char_length = mapping->get(current, 0, chars);
3481 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003482 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003483 if (current_length > Smi::kMaxValue) {
3484 Top::context()->mark_out_of_memory();
3485 return Failure::OutOfMemoryException();
3486 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003487 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003488 // Try again with the real length.
3489 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003490 } else {
3491 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003492 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003493 i++;
3494 }
3495 has_changed_character = true;
3496 }
3497 current = next;
3498 }
3499 if (has_changed_character) {
3500 return result;
3501 } else {
3502 // If we didn't actually change anything in doing the conversion
3503 // we simple return the result and let the converted string
3504 // become garbage; there is no reason to keep two identical strings
3505 // alive.
3506 return s;
3507 }
3508}
3509
3510
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003511template <class Converter>
3512static Object* ConvertCase(Arguments args,
3513 unibrow::Mapping<Converter, 128>* mapping) {
3514 NoHandleAllocation ha;
3515
3516 CONVERT_CHECKED(String, s, args[0]);
3517 s->TryFlattenIfNotFlat();
3518
3519 int input_string_length = s->length();
3520 // Assume that the string is not empty; we need this assumption later
3521 if (input_string_length == 0) return s;
3522 int length = input_string_length;
3523
3524 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3525 if (answer->IsSmi()) {
3526 // Retry with correct length.
3527 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3528 }
3529 return answer; // This may be a failure.
3530}
3531
3532
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003533static Object* Runtime_StringToLowerCase(Arguments args) {
3534 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3535}
3536
3537
3538static Object* Runtime_StringToUpperCase(Arguments args) {
3539 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3540}
3541
3542
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003543bool Runtime::IsUpperCaseChar(uint16_t ch) {
3544 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3545 int char_length = to_upper_mapping.get(ch, 0, chars);
3546 return char_length == 0;
3547}
3548
3549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003550static Object* Runtime_NumberToString(Arguments args) {
3551 NoHandleAllocation ha;
3552 ASSERT(args.length() == 1);
3553
3554 Object* number = args[0];
3555 RUNTIME_ASSERT(number->IsNumber());
3556
3557 Object* cached = Heap::GetNumberStringCache(number);
3558 if (cached != Heap::undefined_value()) {
3559 return cached;
3560 }
3561
3562 char arr[100];
3563 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3564 const char* str;
3565 if (number->IsSmi()) {
3566 int num = Smi::cast(number)->value();
3567 str = IntToCString(num, buffer);
3568 } else {
3569 double num = HeapNumber::cast(number)->value();
3570 str = DoubleToCString(num, buffer);
3571 }
3572 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3573
3574 if (!result->IsFailure()) {
3575 Heap::SetNumberStringCache(number, String::cast(result));
3576 }
3577 return result;
3578}
3579
3580
3581static Object* Runtime_NumberToInteger(Arguments args) {
3582 NoHandleAllocation ha;
3583 ASSERT(args.length() == 1);
3584
3585 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003586 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003587 CONVERT_DOUBLE_CHECKED(number, obj);
3588 return Heap::NumberFromDouble(DoubleToInteger(number));
3589}
3590
3591
3592static Object* Runtime_NumberToJSUint32(Arguments args) {
3593 NoHandleAllocation ha;
3594 ASSERT(args.length() == 1);
3595
3596 Object* obj = args[0];
3597 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3598 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3599 return Heap::NumberFromUint32(number);
3600}
3601
3602
3603static Object* Runtime_NumberToJSInt32(Arguments args) {
3604 NoHandleAllocation ha;
3605 ASSERT(args.length() == 1);
3606
3607 Object* obj = args[0];
3608 if (obj->IsSmi()) return obj;
3609 CONVERT_DOUBLE_CHECKED(number, obj);
3610 return Heap::NumberFromInt32(DoubleToInt32(number));
3611}
3612
3613
ager@chromium.org870a0b62008-11-04 11:43:05 +00003614// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3615// a small integer.
3616static Object* Runtime_NumberToSmi(Arguments args) {
3617 NoHandleAllocation ha;
3618 ASSERT(args.length() == 1);
3619
3620 Object* obj = args[0];
3621 if (obj->IsSmi()) {
3622 return obj;
3623 }
3624 if (obj->IsHeapNumber()) {
3625 double value = HeapNumber::cast(obj)->value();
3626 int int_value = FastD2I(value);
3627 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3628 return Smi::FromInt(int_value);
3629 }
3630 }
3631 return Heap::nan_value();
3632}
3633
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003634
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003635static Object* Runtime_NumberAdd(Arguments args) {
3636 NoHandleAllocation ha;
3637 ASSERT(args.length() == 2);
3638
3639 CONVERT_DOUBLE_CHECKED(x, args[0]);
3640 CONVERT_DOUBLE_CHECKED(y, args[1]);
3641 return Heap::AllocateHeapNumber(x + y);
3642}
3643
3644
3645static Object* Runtime_NumberSub(Arguments args) {
3646 NoHandleAllocation ha;
3647 ASSERT(args.length() == 2);
3648
3649 CONVERT_DOUBLE_CHECKED(x, args[0]);
3650 CONVERT_DOUBLE_CHECKED(y, args[1]);
3651 return Heap::AllocateHeapNumber(x - y);
3652}
3653
3654
3655static Object* Runtime_NumberMul(Arguments args) {
3656 NoHandleAllocation ha;
3657 ASSERT(args.length() == 2);
3658
3659 CONVERT_DOUBLE_CHECKED(x, args[0]);
3660 CONVERT_DOUBLE_CHECKED(y, args[1]);
3661 return Heap::AllocateHeapNumber(x * y);
3662}
3663
3664
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003665static Object* Runtime_NumberUnaryMinus(Arguments args) {
3666 NoHandleAllocation ha;
3667 ASSERT(args.length() == 1);
3668
3669 CONVERT_DOUBLE_CHECKED(x, args[0]);
3670 return Heap::AllocateHeapNumber(-x);
3671}
3672
3673
3674static Object* Runtime_NumberDiv(Arguments args) {
3675 NoHandleAllocation ha;
3676 ASSERT(args.length() == 2);
3677
3678 CONVERT_DOUBLE_CHECKED(x, args[0]);
3679 CONVERT_DOUBLE_CHECKED(y, args[1]);
3680 return Heap::NewNumberFromDouble(x / y);
3681}
3682
3683
3684static Object* Runtime_NumberMod(Arguments args) {
3685 NoHandleAllocation ha;
3686 ASSERT(args.length() == 2);
3687
3688 CONVERT_DOUBLE_CHECKED(x, args[0]);
3689 CONVERT_DOUBLE_CHECKED(y, args[1]);
3690
3691#ifdef WIN32
3692 // Workaround MS fmod bugs. ECMA-262 says:
3693 // dividend is finite and divisor is an infinity => result equals dividend
3694 // dividend is a zero and divisor is nonzero finite => result equals dividend
3695 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3696 !(x == 0 && (y != 0 && isfinite(y))))
3697#endif
3698 x = fmod(x, y);
3699 // NewNumberFromDouble may return a Smi instead of a Number object
3700 return Heap::NewNumberFromDouble(x);
3701}
3702
3703
3704static Object* Runtime_StringAdd(Arguments args) {
3705 NoHandleAllocation ha;
3706 ASSERT(args.length() == 2);
3707
3708 CONVERT_CHECKED(String, str1, args[0]);
3709 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003710 int len1 = str1->length();
3711 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003712 if (len1 == 0) return str2;
3713 if (len2 == 0) return str1;
3714 int length_sum = len1 + len2;
3715 // Make sure that an out of memory exception is thrown if the length
3716 // of the new cons string is too large to fit in a Smi.
3717 if (length_sum > Smi::kMaxValue || length_sum < 0) {
3718 Top::context()->mark_out_of_memory();
3719 return Failure::OutOfMemoryException();
3720 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003721 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003722}
3723
3724
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003725template<typename sinkchar>
3726static inline void StringBuilderConcatHelper(String* special,
3727 sinkchar* sink,
3728 FixedArray* fixed_array,
3729 int array_length) {
3730 int position = 0;
3731 for (int i = 0; i < array_length; i++) {
3732 Object* element = fixed_array->get(i);
3733 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003734 int encoded_slice = Smi::cast(element)->value();
3735 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3736 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003737 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003738 sink + position,
3739 pos,
3740 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003741 position += len;
3742 } else {
3743 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003744 int element_length = string->length();
3745 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003746 position += element_length;
3747 }
3748 }
3749}
3750
3751
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003752static Object* Runtime_StringBuilderConcat(Arguments args) {
3753 NoHandleAllocation ha;
3754 ASSERT(args.length() == 2);
3755 CONVERT_CHECKED(JSArray, array, args[0]);
3756 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003757 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003758 Object* smi_array_length = array->length();
3759 if (!smi_array_length->IsSmi()) {
3760 Top::context()->mark_out_of_memory();
3761 return Failure::OutOfMemoryException();
3762 }
3763 int array_length = Smi::cast(smi_array_length)->value();
3764 if (!array->HasFastElements()) {
3765 return Top::Throw(Heap::illegal_argument_symbol());
3766 }
3767 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003768 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003769 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003770 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003771
3772 if (array_length == 0) {
3773 return Heap::empty_string();
3774 } else if (array_length == 1) {
3775 Object* first = fixed_array->get(0);
3776 if (first->IsString()) return first;
3777 }
3778
ager@chromium.org5ec48922009-05-05 07:25:34 +00003779 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003780 int position = 0;
3781 for (int i = 0; i < array_length; i++) {
3782 Object* elt = fixed_array->get(i);
3783 if (elt->IsSmi()) {
3784 int len = Smi::cast(elt)->value();
3785 int pos = len >> 11;
3786 len &= 0x7ff;
3787 if (pos + len > special_length) {
3788 return Top::Throw(Heap::illegal_argument_symbol());
3789 }
3790 position += len;
3791 } else if (elt->IsString()) {
3792 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003793 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003794 if (!Smi::IsValid(element_length + position)) {
3795 Top::context()->mark_out_of_memory();
3796 return Failure::OutOfMemoryException();
3797 }
3798 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003799 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003800 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003801 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003802 } else {
3803 return Top::Throw(Heap::illegal_argument_symbol());
3804 }
3805 }
3806
3807 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003808 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003809
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003810 if (ascii) {
3811 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003812 if (object->IsFailure()) return object;
3813 SeqAsciiString* answer = SeqAsciiString::cast(object);
3814 StringBuilderConcatHelper(special,
3815 answer->GetChars(),
3816 fixed_array,
3817 array_length);
3818 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003819 } else {
3820 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003821 if (object->IsFailure()) return object;
3822 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3823 StringBuilderConcatHelper(special,
3824 answer->GetChars(),
3825 fixed_array,
3826 array_length);
3827 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003828 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003829}
3830
3831
3832static Object* Runtime_NumberOr(Arguments args) {
3833 NoHandleAllocation ha;
3834 ASSERT(args.length() == 2);
3835
3836 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3837 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3838 return Heap::NumberFromInt32(x | y);
3839}
3840
3841
3842static Object* Runtime_NumberAnd(Arguments args) {
3843 NoHandleAllocation ha;
3844 ASSERT(args.length() == 2);
3845
3846 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3847 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3848 return Heap::NumberFromInt32(x & y);
3849}
3850
3851
3852static Object* Runtime_NumberXor(Arguments args) {
3853 NoHandleAllocation ha;
3854 ASSERT(args.length() == 2);
3855
3856 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3857 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3858 return Heap::NumberFromInt32(x ^ y);
3859}
3860
3861
3862static Object* Runtime_NumberNot(Arguments args) {
3863 NoHandleAllocation ha;
3864 ASSERT(args.length() == 1);
3865
3866 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3867 return Heap::NumberFromInt32(~x);
3868}
3869
3870
3871static Object* Runtime_NumberShl(Arguments args) {
3872 NoHandleAllocation ha;
3873 ASSERT(args.length() == 2);
3874
3875 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3876 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3877 return Heap::NumberFromInt32(x << (y & 0x1f));
3878}
3879
3880
3881static Object* Runtime_NumberShr(Arguments args) {
3882 NoHandleAllocation ha;
3883 ASSERT(args.length() == 2);
3884
3885 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3886 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3887 return Heap::NumberFromUint32(x >> (y & 0x1f));
3888}
3889
3890
3891static Object* Runtime_NumberSar(Arguments args) {
3892 NoHandleAllocation ha;
3893 ASSERT(args.length() == 2);
3894
3895 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3896 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3897 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3898}
3899
3900
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003901static Object* Runtime_NumberEquals(Arguments args) {
3902 NoHandleAllocation ha;
3903 ASSERT(args.length() == 2);
3904
3905 CONVERT_DOUBLE_CHECKED(x, args[0]);
3906 CONVERT_DOUBLE_CHECKED(y, args[1]);
3907 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3908 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3909 if (x == y) return Smi::FromInt(EQUAL);
3910 Object* result;
3911 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3912 result = Smi::FromInt(EQUAL);
3913 } else {
3914 result = Smi::FromInt(NOT_EQUAL);
3915 }
3916 return result;
3917}
3918
3919
3920static Object* Runtime_StringEquals(Arguments args) {
3921 NoHandleAllocation ha;
3922 ASSERT(args.length() == 2);
3923
3924 CONVERT_CHECKED(String, x, args[0]);
3925 CONVERT_CHECKED(String, y, args[1]);
3926
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003927 bool not_equal = !x->Equals(y);
3928 // This is slightly convoluted because the value that signifies
3929 // equality is 0 and inequality is 1 so we have to negate the result
3930 // from String::Equals.
3931 ASSERT(not_equal == 0 || not_equal == 1);
3932 STATIC_CHECK(EQUAL == 0);
3933 STATIC_CHECK(NOT_EQUAL == 1);
3934 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935}
3936
3937
3938static Object* Runtime_NumberCompare(Arguments args) {
3939 NoHandleAllocation ha;
3940 ASSERT(args.length() == 3);
3941
3942 CONVERT_DOUBLE_CHECKED(x, args[0]);
3943 CONVERT_DOUBLE_CHECKED(y, args[1]);
3944 if (isnan(x) || isnan(y)) return args[2];
3945 if (x == y) return Smi::FromInt(EQUAL);
3946 if (isless(x, y)) return Smi::FromInt(LESS);
3947 return Smi::FromInt(GREATER);
3948}
3949
3950
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003951// Compare two Smis as if they were converted to strings and then
3952// compared lexicographically.
3953static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3954 NoHandleAllocation ha;
3955 ASSERT(args.length() == 2);
3956
3957 // Arrays for the individual characters of the two Smis. Smis are
3958 // 31 bit integers and 10 decimal digits are therefore enough.
3959 static int x_elms[10];
3960 static int y_elms[10];
3961
3962 // Extract the integer values from the Smis.
3963 CONVERT_CHECKED(Smi, x, args[0]);
3964 CONVERT_CHECKED(Smi, y, args[1]);
3965 int x_value = x->value();
3966 int y_value = y->value();
3967
3968 // If the integers are equal so are the string representations.
3969 if (x_value == y_value) return Smi::FromInt(EQUAL);
3970
3971 // If one of the integers are zero the normal integer order is the
3972 // same as the lexicographic order of the string representations.
3973 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3974
ager@chromium.org32912102009-01-16 10:38:43 +00003975 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003976 // smallest because the char code of '-' is less than the char code
3977 // of any digit. Otherwise, we make both values positive.
3978 if (x_value < 0 || y_value < 0) {
3979 if (y_value >= 0) return Smi::FromInt(LESS);
3980 if (x_value >= 0) return Smi::FromInt(GREATER);
3981 x_value = -x_value;
3982 y_value = -y_value;
3983 }
3984
3985 // Convert the integers to arrays of their decimal digits.
3986 int x_index = 0;
3987 int y_index = 0;
3988 while (x_value > 0) {
3989 x_elms[x_index++] = x_value % 10;
3990 x_value /= 10;
3991 }
3992 while (y_value > 0) {
3993 y_elms[y_index++] = y_value % 10;
3994 y_value /= 10;
3995 }
3996
3997 // Loop through the arrays of decimal digits finding the first place
3998 // where they differ.
3999 while (--x_index >= 0 && --y_index >= 0) {
4000 int diff = x_elms[x_index] - y_elms[y_index];
4001 if (diff != 0) return Smi::FromInt(diff);
4002 }
4003
4004 // If one array is a suffix of the other array, the longest array is
4005 // the representation of the largest of the Smis in the
4006 // lexicographic ordering.
4007 return Smi::FromInt(x_index - y_index);
4008}
4009
4010
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004011static Object* Runtime_StringCompare(Arguments args) {
4012 NoHandleAllocation ha;
4013 ASSERT(args.length() == 2);
4014
4015 CONVERT_CHECKED(String, x, args[0]);
4016 CONVERT_CHECKED(String, y, args[1]);
4017
4018 // A few fast case tests before we flatten.
4019 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004020 if (y->length() == 0) {
4021 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004022 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004023 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004024 return Smi::FromInt(LESS);
4025 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004026
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004027 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004028 if (d < 0) return Smi::FromInt(LESS);
4029 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004030
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004031 x->TryFlattenIfNotFlat();
4032 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004033
4034 static StringInputBuffer bufx;
4035 static StringInputBuffer bufy;
4036 bufx.Reset(x);
4037 bufy.Reset(y);
4038 while (bufx.has_more() && bufy.has_more()) {
4039 int d = bufx.GetNext() - bufy.GetNext();
4040 if (d < 0) return Smi::FromInt(LESS);
4041 else if (d > 0) return Smi::FromInt(GREATER);
4042 }
4043
4044 // x is (non-trivial) prefix of y:
4045 if (bufy.has_more()) return Smi::FromInt(LESS);
4046 // y is prefix of x:
4047 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4048}
4049
4050
4051static Object* Runtime_Math_abs(Arguments args) {
4052 NoHandleAllocation ha;
4053 ASSERT(args.length() == 1);
4054
4055 CONVERT_DOUBLE_CHECKED(x, args[0]);
4056 return Heap::AllocateHeapNumber(fabs(x));
4057}
4058
4059
4060static Object* Runtime_Math_acos(Arguments args) {
4061 NoHandleAllocation ha;
4062 ASSERT(args.length() == 1);
4063
4064 CONVERT_DOUBLE_CHECKED(x, args[0]);
4065 return Heap::AllocateHeapNumber(acos(x));
4066}
4067
4068
4069static Object* Runtime_Math_asin(Arguments args) {
4070 NoHandleAllocation ha;
4071 ASSERT(args.length() == 1);
4072
4073 CONVERT_DOUBLE_CHECKED(x, args[0]);
4074 return Heap::AllocateHeapNumber(asin(x));
4075}
4076
4077
4078static Object* Runtime_Math_atan(Arguments args) {
4079 NoHandleAllocation ha;
4080 ASSERT(args.length() == 1);
4081
4082 CONVERT_DOUBLE_CHECKED(x, args[0]);
4083 return Heap::AllocateHeapNumber(atan(x));
4084}
4085
4086
4087static Object* Runtime_Math_atan2(Arguments args) {
4088 NoHandleAllocation ha;
4089 ASSERT(args.length() == 2);
4090
4091 CONVERT_DOUBLE_CHECKED(x, args[0]);
4092 CONVERT_DOUBLE_CHECKED(y, args[1]);
4093 double result;
4094 if (isinf(x) && isinf(y)) {
4095 // Make sure that the result in case of two infinite arguments
4096 // is a multiple of Pi / 4. The sign of the result is determined
4097 // by the first argument (x) and the sign of the second argument
4098 // determines the multiplier: one or three.
4099 static double kPiDividedBy4 = 0.78539816339744830962;
4100 int multiplier = (x < 0) ? -1 : 1;
4101 if (y < 0) multiplier *= 3;
4102 result = multiplier * kPiDividedBy4;
4103 } else {
4104 result = atan2(x, y);
4105 }
4106 return Heap::AllocateHeapNumber(result);
4107}
4108
4109
4110static Object* Runtime_Math_ceil(Arguments args) {
4111 NoHandleAllocation ha;
4112 ASSERT(args.length() == 1);
4113
4114 CONVERT_DOUBLE_CHECKED(x, args[0]);
4115 return Heap::NumberFromDouble(ceiling(x));
4116}
4117
4118
4119static Object* Runtime_Math_cos(Arguments args) {
4120 NoHandleAllocation ha;
4121 ASSERT(args.length() == 1);
4122
4123 CONVERT_DOUBLE_CHECKED(x, args[0]);
4124 return Heap::AllocateHeapNumber(cos(x));
4125}
4126
4127
4128static Object* Runtime_Math_exp(Arguments args) {
4129 NoHandleAllocation ha;
4130 ASSERT(args.length() == 1);
4131
4132 CONVERT_DOUBLE_CHECKED(x, args[0]);
4133 return Heap::AllocateHeapNumber(exp(x));
4134}
4135
4136
4137static Object* Runtime_Math_floor(Arguments args) {
4138 NoHandleAllocation ha;
4139 ASSERT(args.length() == 1);
4140
4141 CONVERT_DOUBLE_CHECKED(x, args[0]);
4142 return Heap::NumberFromDouble(floor(x));
4143}
4144
4145
4146static Object* Runtime_Math_log(Arguments args) {
4147 NoHandleAllocation ha;
4148 ASSERT(args.length() == 1);
4149
4150 CONVERT_DOUBLE_CHECKED(x, args[0]);
4151 return Heap::AllocateHeapNumber(log(x));
4152}
4153
4154
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004155// Helper function to compute x^y, where y is known to be an
4156// integer. Uses binary decomposition to limit the number of
4157// multiplications; see the discussion in "Hacker's Delight" by Henry
4158// S. Warren, Jr., figure 11-6, page 213.
4159static double powi(double x, int y) {
4160 ASSERT(y != kMinInt);
4161 unsigned n = (y < 0) ? -y : y;
4162 double m = x;
4163 double p = 1;
4164 while (true) {
4165 if ((n & 1) != 0) p *= m;
4166 n >>= 1;
4167 if (n == 0) {
4168 if (y < 0) {
4169 // Unfortunately, we have to be careful when p has reached
4170 // infinity in the computation, because sometimes the higher
4171 // internal precision in the pow() implementation would have
4172 // given us a finite p. This happens very rarely.
4173 double result = 1.0 / p;
4174 return (result == 0 && isinf(p))
4175 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4176 : result;
4177 } else {
4178 return p;
4179 }
4180 }
4181 m *= m;
4182 }
4183}
4184
4185
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004186static Object* Runtime_Math_pow(Arguments args) {
4187 NoHandleAllocation ha;
4188 ASSERT(args.length() == 2);
4189
4190 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004191
4192 // If the second argument is a smi, it is much faster to call the
4193 // custom powi() function than the generic pow().
4194 if (args[1]->IsSmi()) {
4195 int y = Smi::cast(args[1])->value();
4196 return Heap::AllocateHeapNumber(powi(x, y));
4197 }
4198
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004199 CONVERT_DOUBLE_CHECKED(y, args[1]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004200 if (y == 0.5) {
4201 // It's not uncommon to use Math.pow(x, 0.5) to compute the square
4202 // root of a number. To speed up such computations, we explictly
4203 // check for this case and use the sqrt() function which is faster
4204 // than pow().
4205 return Heap::AllocateHeapNumber(sqrt(x));
4206 } else if (y == -0.5) {
4207 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4208 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004209 } else if (y == 0) {
4210 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004211 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4212 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004213 } else {
4214 return Heap::AllocateHeapNumber(pow(x, y));
4215 }
4216}
4217
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004218
4219static Object* Runtime_Math_round(Arguments args) {
4220 NoHandleAllocation ha;
4221 ASSERT(args.length() == 1);
4222
4223 CONVERT_DOUBLE_CHECKED(x, args[0]);
4224 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4225 return Heap::NumberFromDouble(floor(x + 0.5));
4226}
4227
4228
4229static Object* Runtime_Math_sin(Arguments args) {
4230 NoHandleAllocation ha;
4231 ASSERT(args.length() == 1);
4232
4233 CONVERT_DOUBLE_CHECKED(x, args[0]);
4234 return Heap::AllocateHeapNumber(sin(x));
4235}
4236
4237
4238static Object* Runtime_Math_sqrt(Arguments args) {
4239 NoHandleAllocation ha;
4240 ASSERT(args.length() == 1);
4241
4242 CONVERT_DOUBLE_CHECKED(x, args[0]);
4243 return Heap::AllocateHeapNumber(sqrt(x));
4244}
4245
4246
4247static Object* Runtime_Math_tan(Arguments args) {
4248 NoHandleAllocation ha;
4249 ASSERT(args.length() == 1);
4250
4251 CONVERT_DOUBLE_CHECKED(x, args[0]);
4252 return Heap::AllocateHeapNumber(tan(x));
4253}
4254
4255
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004256// The NewArguments function is only used when constructing the
4257// arguments array when calling non-functions from JavaScript in
4258// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004259static Object* Runtime_NewArguments(Arguments args) {
4260 NoHandleAllocation ha;
4261 ASSERT(args.length() == 1);
4262
4263 // ECMA-262, 3rd., 10.1.8, p.39
4264 CONVERT_CHECKED(JSFunction, callee, args[0]);
4265
4266 // Compute the frame holding the arguments.
4267 JavaScriptFrameIterator it;
4268 it.AdvanceToArgumentsFrame();
4269 JavaScriptFrame* frame = it.frame();
4270
4271 const int length = frame->GetProvidedParametersCount();
4272 Object* result = Heap::AllocateArgumentsObject(callee, length);
4273 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004274 if (length > 0) {
4275 Object* obj = Heap::AllocateFixedArray(length);
4276 if (obj->IsFailure()) return obj;
4277 FixedArray* array = FixedArray::cast(obj);
4278 ASSERT(array->length() == length);
4279 WriteBarrierMode mode = array->GetWriteBarrierMode();
4280 for (int i = 0; i < length; i++) {
4281 array->set(i, frame->GetParameter(i), mode);
4282 }
4283 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004284 }
4285 return result;
4286}
4287
4288
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004289static Object* Runtime_NewArgumentsFast(Arguments args) {
4290 NoHandleAllocation ha;
4291 ASSERT(args.length() == 3);
4292
4293 JSFunction* callee = JSFunction::cast(args[0]);
4294 Object** parameters = reinterpret_cast<Object**>(args[1]);
4295 const int length = Smi::cast(args[2])->value();
4296
4297 Object* result = Heap::AllocateArgumentsObject(callee, length);
4298 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004299 ASSERT(Heap::InNewSpace(result));
4300
4301 // Allocate the elements if needed.
4302 if (length > 0) {
4303 // Allocate the fixed array.
4304 Object* obj = Heap::AllocateRawFixedArray(length);
4305 if (obj->IsFailure()) return obj;
4306 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4307 FixedArray* array = FixedArray::cast(obj);
4308 array->set_length(length);
4309 WriteBarrierMode mode = array->GetWriteBarrierMode();
4310 for (int i = 0; i < length; i++) {
4311 array->set(i, *--parameters, mode);
4312 }
4313 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4314 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004315 }
4316 return result;
4317}
4318
4319
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004320static Object* Runtime_NewClosure(Arguments args) {
4321 HandleScope scope;
4322 ASSERT(args.length() == 2);
4323 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4324 CONVERT_ARG_CHECKED(Context, context, 1);
4325
4326 Handle<JSFunction> result =
4327 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4328 return *result;
4329}
4330
4331
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004332static Handle<Code> ComputeConstructStub(Handle<Map> map) {
4333 // TODO(385): Change this to create a construct stub specialized for
4334 // the given map to make allocation of simple objects - and maybe
4335 // arrays - much faster.
4336 return Handle<Code>(Builtins::builtin(Builtins::JSConstructStubGeneric));
4337}
4338
4339
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004340static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004341 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004342 ASSERT(args.length() == 1);
4343
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004344 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004345
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004346 // If the constructor isn't a proper function we throw a type error.
4347 if (!constructor->IsJSFunction()) {
4348 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4349 Handle<Object> type_error =
4350 Factory::NewTypeError("not_constructor", arguments);
4351 return Top::Throw(*type_error);
4352 }
4353
4354 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004355#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004356 // Handle stepping into constructors if step into is active.
4357 if (Debug::StepInActive()) {
4358 Debug::HandleStepIn(function, 0, true);
4359 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004360#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004361
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004362 if (function->has_initial_map()) {
4363 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004364 // The 'Function' function ignores the receiver object when
4365 // called using 'new' and creates a new JSFunction object that
4366 // is returned. The receiver object is only used for error
4367 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004368 // JSFunction. Factory::NewJSObject() should not be used to
4369 // allocate JSFunctions since it does not properly initialize
4370 // the shared part of the function. Since the receiver is
4371 // ignored anyway, we use the global object as the receiver
4372 // instead of a new JSFunction object. This way, errors are
4373 // reported the same way whether or not 'Function' is called
4374 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004375 return Top::context()->global();
4376 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004377 }
4378
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004379 bool first_allocation = !function->has_initial_map();
4380 Handle<JSObject> result = Factory::NewJSObject(function);
4381 if (first_allocation) {
4382 Handle<Map> map = Handle<Map>(function->initial_map());
4383 Handle<Code> stub = ComputeConstructStub(map);
4384 function->shared()->set_construct_stub(*stub);
4385 }
4386 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004387}
4388
4389
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004390static Object* Runtime_LazyCompile(Arguments args) {
4391 HandleScope scope;
4392 ASSERT(args.length() == 1);
4393
4394 Handle<JSFunction> function = args.at<JSFunction>(0);
4395#ifdef DEBUG
4396 if (FLAG_trace_lazy) {
4397 PrintF("[lazy: ");
4398 function->shared()->name()->Print();
4399 PrintF("]\n");
4400 }
4401#endif
4402
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004403 // Compile the target function. Here we compile using CompileLazyInLoop in
4404 // order to get the optimized version. This helps code like delta-blue
4405 // that calls performance-critical routines through constructors. A
4406 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4407 // direct call. Since the in-loop tracking takes place through CallICs
4408 // this means that things called through constructors are never known to
4409 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004411 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004412 return Failure::Exception();
4413 }
4414
4415 return function->code();
4416}
4417
4418
4419static Object* Runtime_GetCalledFunction(Arguments args) {
4420 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004421 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004422 StackFrameIterator it;
4423 // Get past the JS-to-C exit frame.
4424 ASSERT(it.frame()->is_exit());
4425 it.Advance();
4426 // Get past the CALL_NON_FUNCTION activation frame.
4427 ASSERT(it.frame()->is_java_script());
4428 it.Advance();
4429 // Argument adaptor frames do not copy the function; we have to skip
4430 // past them to get to the real calling frame.
4431 if (it.frame()->is_arguments_adaptor()) it.Advance();
4432 // Get the function from the top of the expression stack of the
4433 // calling frame.
4434 StandardFrame* frame = StandardFrame::cast(it.frame());
4435 int index = frame->ComputeExpressionsCount() - 1;
4436 Object* result = frame->GetExpression(index);
4437 return result;
4438}
4439
4440
4441static Object* Runtime_GetFunctionDelegate(Arguments args) {
4442 HandleScope scope;
4443 ASSERT(args.length() == 1);
4444 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4445 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4446}
4447
4448
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004449static Object* Runtime_GetConstructorDelegate(Arguments args) {
4450 HandleScope scope;
4451 ASSERT(args.length() == 1);
4452 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4453 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4454}
4455
4456
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004457static Object* Runtime_NewContext(Arguments args) {
4458 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004459 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004460
kasper.lund7276f142008-07-30 08:49:36 +00004461 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004462 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4463 Object* result = Heap::AllocateFunctionContext(length, function);
4464 if (result->IsFailure()) return result;
4465
4466 Top::set_context(Context::cast(result));
4467
kasper.lund7276f142008-07-30 08:49:36 +00004468 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004469}
4470
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004471static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004472 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004473 Object* js_object = object;
4474 if (!js_object->IsJSObject()) {
4475 js_object = js_object->ToObject();
4476 if (js_object->IsFailure()) {
4477 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004478 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004479 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004480 Handle<Object> result =
4481 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4482 return Top::Throw(*result);
4483 }
4484 }
4485
4486 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004487 Heap::AllocateWithContext(Top::context(),
4488 JSObject::cast(js_object),
4489 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004490 if (result->IsFailure()) return result;
4491
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004492 Context* context = Context::cast(result);
4493 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004494
kasper.lund7276f142008-07-30 08:49:36 +00004495 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004496}
4497
4498
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004499static Object* Runtime_PushContext(Arguments args) {
4500 NoHandleAllocation ha;
4501 ASSERT(args.length() == 1);
4502 return PushContextHelper(args[0], false);
4503}
4504
4505
4506static Object* Runtime_PushCatchContext(Arguments args) {
4507 NoHandleAllocation ha;
4508 ASSERT(args.length() == 1);
4509 return PushContextHelper(args[0], true);
4510}
4511
4512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004513static Object* Runtime_LookupContext(Arguments args) {
4514 HandleScope scope;
4515 ASSERT(args.length() == 2);
4516
4517 CONVERT_ARG_CHECKED(Context, context, 0);
4518 CONVERT_ARG_CHECKED(String, name, 1);
4519
4520 int index;
4521 PropertyAttributes attributes;
4522 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004523 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004524 context->Lookup(name, flags, &index, &attributes);
4525
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004526 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004527 ASSERT(holder->IsJSObject());
4528 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004529 }
4530
4531 // No intermediate context found. Use global object by default.
4532 return Top::context()->global();
4533}
4534
4535
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004536// A mechanism to return pairs of Object*'s. This is somewhat
4537// compiler-dependent as it assumes that a 64-bit value (a long long)
4538// is returned via two registers (edx:eax on ia32). Both the ia32 and
4539// arm platform support this; it is mostly an issue of "coaxing" the
4540// compiler to do the right thing.
4541//
4542// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004543// TODO(x64): Definitely!
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004544typedef uint64_t ObjectPair;
4545static inline ObjectPair MakePair(Object* x, Object* y) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004546#if V8_HOST_ARCH_64_BIT
4547 UNIMPLEMENTED();
4548 return 0;
4549#else
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004550 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004551 (reinterpret_cast<ObjectPair>(y) << 32);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004552#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004553}
4554
4555
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004556static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004557 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4558 USE(attributes);
4559 return x->IsTheHole() ? Heap::undefined_value() : x;
4560}
4561
4562
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004563static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4564 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004565 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004566 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004567 JSFunction* context_extension_function =
4568 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004569 // If the holder isn't a context extension object, we just return it
4570 // as the receiver. This allows arguments objects to be used as
4571 // receivers, but only if they are put in the context scope chain
4572 // explicitly via a with-statement.
4573 Object* constructor = holder->map()->constructor();
4574 if (constructor != context_extension_function) return holder;
4575 // Fall back to using the global object as the receiver if the
4576 // property turns out to be a local variable allocated in a context
4577 // extension object - introduced via eval.
4578 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004579}
4580
4581
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004582static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004583 HandleScope scope;
4584 ASSERT(args.length() == 2);
4585
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004586 if (!args[0]->IsContext() || !args[1]->IsString()) {
4587 return MakePair(IllegalOperation(), NULL);
4588 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004589 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004590 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591
4592 int index;
4593 PropertyAttributes attributes;
4594 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004595 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004596 context->Lookup(name, flags, &index, &attributes);
4597
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004598 // If the index is non-negative, the slot has been found in a local
4599 // variable or a parameter. Read it from the context object or the
4600 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004601 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004602 // If the "property" we were looking for is a local variable or an
4603 // argument in a context, the receiver is the global object; see
4604 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4605 JSObject* receiver = Top::context()->global()->global_receiver();
4606 Object* value = (holder->IsContext())
4607 ? Context::cast(*holder)->get(index)
4608 : JSObject::cast(*holder)->GetElement(index);
4609 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004610 }
4611
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004612 // If the holder is found, we read the property from it.
4613 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004614 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004615 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004616 JSObject* receiver;
4617 if (object->IsGlobalObject()) {
4618 receiver = GlobalObject::cast(object)->global_receiver();
4619 } else if (context->is_exception_holder(*holder)) {
4620 receiver = Top::context()->global()->global_receiver();
4621 } else {
4622 receiver = ComputeReceiverForNonGlobal(object);
4623 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004624 // No need to unhole the value here. This is taken care of by the
4625 // GetProperty function.
4626 Object* value = object->GetProperty(*name);
4627 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004628 }
4629
4630 if (throw_error) {
4631 // The property doesn't exist - throw exception.
4632 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004633 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004634 return MakePair(Top::Throw(*reference_error), NULL);
4635 } else {
4636 // The property doesn't exist - return undefined
4637 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4638 }
4639}
4640
4641
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004642static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004643 return LoadContextSlotHelper(args, true);
4644}
4645
4646
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004647static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004648 return LoadContextSlotHelper(args, false);
4649}
4650
4651
4652static Object* Runtime_StoreContextSlot(Arguments args) {
4653 HandleScope scope;
4654 ASSERT(args.length() == 3);
4655
4656 Handle<Object> value(args[0]);
4657 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004658 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004659
4660 int index;
4661 PropertyAttributes attributes;
4662 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004663 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004664 context->Lookup(name, flags, &index, &attributes);
4665
4666 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004667 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004668 // Ignore if read_only variable.
4669 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004670 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004671 }
4672 } else {
4673 ASSERT((attributes & READ_ONLY) == 0);
4674 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004675 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004676 USE(result);
4677 ASSERT(!result->IsFailure());
4678 }
4679 return *value;
4680 }
4681
4682 // Slow case: The property is not in a FixedArray context.
4683 // It is either in an JSObject extension context or it was not found.
4684 Handle<JSObject> context_ext;
4685
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004686 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004687 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004688 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004689 } else {
4690 // The property was not found. It needs to be stored in the global context.
4691 ASSERT(attributes == ABSENT);
4692 attributes = NONE;
4693 context_ext = Handle<JSObject>(Top::context()->global());
4694 }
4695
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004696 // Set the property, but ignore if read_only variable on the context
4697 // extension object itself.
4698 if ((attributes & READ_ONLY) == 0 ||
4699 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004700 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4701 if (set.is_null()) {
4702 // Failure::Exception is converted to a null handle in the
4703 // handle-based methods such as SetProperty. We therefore need
4704 // to convert null handles back to exceptions.
4705 ASSERT(Top::has_pending_exception());
4706 return Failure::Exception();
4707 }
4708 }
4709 return *value;
4710}
4711
4712
4713static Object* Runtime_Throw(Arguments args) {
4714 HandleScope scope;
4715 ASSERT(args.length() == 1);
4716
4717 return Top::Throw(args[0]);
4718}
4719
4720
4721static Object* Runtime_ReThrow(Arguments args) {
4722 HandleScope scope;
4723 ASSERT(args.length() == 1);
4724
4725 return Top::ReThrow(args[0]);
4726}
4727
4728
4729static Object* Runtime_ThrowReferenceError(Arguments args) {
4730 HandleScope scope;
4731 ASSERT(args.length() == 1);
4732
4733 Handle<Object> name(args[0]);
4734 Handle<Object> reference_error =
4735 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4736 return Top::Throw(*reference_error);
4737}
4738
4739
4740static Object* Runtime_StackOverflow(Arguments args) {
4741 NoHandleAllocation na;
4742 return Top::StackOverflow();
4743}
4744
4745
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004746static Object* Runtime_StackGuard(Arguments args) {
4747 ASSERT(args.length() == 1);
4748
4749 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004750 if (StackGuard::IsStackOverflow()) {
4751 return Runtime_StackOverflow(args);
4752 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004753
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004754 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004755}
4756
4757
4758// NOTE: These PrintXXX functions are defined for all builds (not just
4759// DEBUG builds) because we may want to be able to trace function
4760// calls in all modes.
4761static void PrintString(String* str) {
4762 // not uncommon to have empty strings
4763 if (str->length() > 0) {
4764 SmartPointer<char> s =
4765 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4766 PrintF("%s", *s);
4767 }
4768}
4769
4770
4771static void PrintObject(Object* obj) {
4772 if (obj->IsSmi()) {
4773 PrintF("%d", Smi::cast(obj)->value());
4774 } else if (obj->IsString() || obj->IsSymbol()) {
4775 PrintString(String::cast(obj));
4776 } else if (obj->IsNumber()) {
4777 PrintF("%g", obj->Number());
4778 } else if (obj->IsFailure()) {
4779 PrintF("<failure>");
4780 } else if (obj->IsUndefined()) {
4781 PrintF("<undefined>");
4782 } else if (obj->IsNull()) {
4783 PrintF("<null>");
4784 } else if (obj->IsTrue()) {
4785 PrintF("<true>");
4786 } else if (obj->IsFalse()) {
4787 PrintF("<false>");
4788 } else {
4789 PrintF("%p", obj);
4790 }
4791}
4792
4793
4794static int StackSize() {
4795 int n = 0;
4796 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4797 return n;
4798}
4799
4800
4801static void PrintTransition(Object* result) {
4802 // indentation
4803 { const int nmax = 80;
4804 int n = StackSize();
4805 if (n <= nmax)
4806 PrintF("%4d:%*s", n, n, "");
4807 else
4808 PrintF("%4d:%*s", n, nmax, "...");
4809 }
4810
4811 if (result == NULL) {
4812 // constructor calls
4813 JavaScriptFrameIterator it;
4814 JavaScriptFrame* frame = it.frame();
4815 if (frame->IsConstructor()) PrintF("new ");
4816 // function name
4817 Object* fun = frame->function();
4818 if (fun->IsJSFunction()) {
4819 PrintObject(JSFunction::cast(fun)->shared()->name());
4820 } else {
4821 PrintObject(fun);
4822 }
4823 // function arguments
4824 // (we are intentionally only printing the actually
4825 // supplied parameters, not all parameters required)
4826 PrintF("(this=");
4827 PrintObject(frame->receiver());
4828 const int length = frame->GetProvidedParametersCount();
4829 for (int i = 0; i < length; i++) {
4830 PrintF(", ");
4831 PrintObject(frame->GetParameter(i));
4832 }
4833 PrintF(") {\n");
4834
4835 } else {
4836 // function result
4837 PrintF("} -> ");
4838 PrintObject(result);
4839 PrintF("\n");
4840 }
4841}
4842
4843
4844static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004845 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004846 NoHandleAllocation ha;
4847 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004848 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004849}
4850
4851
4852static Object* Runtime_TraceExit(Arguments args) {
4853 NoHandleAllocation ha;
4854 PrintTransition(args[0]);
4855 return args[0]; // return TOS
4856}
4857
4858
4859static Object* Runtime_DebugPrint(Arguments args) {
4860 NoHandleAllocation ha;
4861 ASSERT(args.length() == 1);
4862
4863#ifdef DEBUG
4864 if (args[0]->IsString()) {
4865 // If we have a string, assume it's a code "marker"
4866 // and print some interesting cpu debugging info.
4867 JavaScriptFrameIterator it;
4868 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004869 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4870 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004871 } else {
4872 PrintF("DebugPrint: ");
4873 }
4874 args[0]->Print();
4875#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004876 // ShortPrint is available in release mode. Print is not.
4877 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878#endif
4879 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004880 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004881
4882 return args[0]; // return TOS
4883}
4884
4885
4886static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004887 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004888 NoHandleAllocation ha;
4889 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004890 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004891}
4892
4893
mads.s.ager31e71382008-08-13 09:32:07 +00004894static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004896 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004897
4898 // According to ECMA-262, section 15.9.1, page 117, the precision of
4899 // the number in a Date object representing a particular instant in
4900 // time is milliseconds. Therefore, we floor the result of getting
4901 // the OS time.
4902 double millis = floor(OS::TimeCurrentMillis());
4903 return Heap::NumberFromDouble(millis);
4904}
4905
4906
4907static Object* Runtime_DateParseString(Arguments args) {
4908 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004909 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004910
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004911 CONVERT_ARG_CHECKED(String, str, 0);
4912 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004913
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004914 CONVERT_ARG_CHECKED(JSArray, output, 1);
4915 RUNTIME_ASSERT(output->HasFastElements());
4916
4917 AssertNoAllocation no_allocation;
4918
4919 FixedArray* output_array = output->elements();
4920 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4921 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004922 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004923 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004924 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004925 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004926 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4927 }
4928
4929 if (result) {
4930 return *output;
4931 } else {
4932 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004933 }
4934}
4935
4936
4937static Object* Runtime_DateLocalTimezone(Arguments args) {
4938 NoHandleAllocation ha;
4939 ASSERT(args.length() == 1);
4940
4941 CONVERT_DOUBLE_CHECKED(x, args[0]);
4942 char* zone = OS::LocalTimezone(x);
4943 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4944}
4945
4946
4947static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4948 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004949 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004950
4951 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4952}
4953
4954
4955static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4956 NoHandleAllocation ha;
4957 ASSERT(args.length() == 1);
4958
4959 CONVERT_DOUBLE_CHECKED(x, args[0]);
4960 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4961}
4962
4963
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004964static Object* Runtime_NumberIsFinite(Arguments args) {
4965 NoHandleAllocation ha;
4966 ASSERT(args.length() == 1);
4967
4968 CONVERT_DOUBLE_CHECKED(value, args[0]);
4969 Object* result;
4970 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4971 result = Heap::false_value();
4972 } else {
4973 result = Heap::true_value();
4974 }
4975 return result;
4976}
4977
4978
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004979static Object* Runtime_GlobalReceiver(Arguments args) {
4980 ASSERT(args.length() == 1);
4981 Object* global = args[0];
4982 if (!global->IsJSGlobalObject()) return Heap::null_value();
4983 return JSGlobalObject::cast(global)->global_receiver();
4984}
4985
4986
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004987static Object* Runtime_CompileString(Arguments args) {
4988 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004989 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004990 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004991 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004992
ager@chromium.org381abbb2009-02-25 13:23:22 +00004993 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004994 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004995 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4996 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004997 true,
4998 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004999 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005000 Handle<JSFunction> fun =
5001 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5002 return *fun;
5003}
5004
5005
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005006static Handle<JSFunction> GetBuiltinFunction(String* name) {
5007 LookupResult result;
5008 Top::global_context()->builtins()->LocalLookup(name, &result);
5009 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5010}
5011
5012
5013static Object* CompileDirectEval(Handle<String> source) {
5014 // Compute the eval context.
5015 HandleScope scope;
5016 StackFrameLocator locator;
5017 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5018 Handle<Context> context(Context::cast(frame->context()));
5019 bool is_global = context->IsGlobalContext();
5020
ager@chromium.org381abbb2009-02-25 13:23:22 +00005021 // Compile source string in the current context.
5022 Handle<JSFunction> boilerplate =
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005023 Compiler::CompileEval(source, context, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005024 if (boilerplate.is_null()) return Failure::Exception();
5025 Handle<JSFunction> fun =
5026 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5027 return *fun;
5028}
5029
5030
5031static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5032 ASSERT(args.length() == 2);
5033
5034 HandleScope scope;
5035
5036 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5037
5038 Handle<Object> receiver;
5039
5040 // Find where the 'eval' symbol is bound. It is unaliased only if
5041 // it is bound in the global context.
5042 StackFrameLocator locator;
5043 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5044 Handle<Context> context(Context::cast(frame->context()));
5045 int index;
5046 PropertyAttributes attributes;
5047 while (!context.is_null()) {
5048 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5049 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005050 // Stop search when eval is found or when the global context is
5051 // reached.
5052 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005053 if (context->is_function_context()) {
5054 context = Handle<Context>(Context::cast(context->closure()->context()));
5055 } else {
5056 context = Handle<Context>(context->previous());
5057 }
5058 }
5059
iposva@chromium.org245aa852009-02-10 00:49:54 +00005060 // If eval could not be resolved, it has been deleted and we need to
5061 // throw a reference error.
5062 if (attributes == ABSENT) {
5063 Handle<Object> name = Factory::eval_symbol();
5064 Handle<Object> reference_error =
5065 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5066 return Top::Throw(*reference_error);
5067 }
5068
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005069 if (context->IsGlobalContext()) {
5070 // 'eval' is bound in the global context, but it may have been overwritten.
5071 // Compare it to the builtin 'GlobalEval' function to make sure.
5072 Handle<JSFunction> global_eval =
5073 GetBuiltinFunction(Heap::global_eval_symbol());
5074 if (global_eval.is_identical_to(callee)) {
5075 // A direct eval call.
5076 if (args[1]->IsString()) {
5077 CONVERT_ARG_CHECKED(String, source, 1);
5078 // A normal eval call on a string. Compile it and return the
5079 // compiled function bound in the local context.
5080 Object* compiled_source = CompileDirectEval(source);
5081 if (compiled_source->IsFailure()) return compiled_source;
5082 receiver = Handle<Object>(frame->receiver());
5083 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5084 } else {
5085 // An eval call that is not called on a string. Global eval
5086 // deals better with this.
5087 receiver = Handle<Object>(Top::global_context()->global());
5088 }
5089 } else {
5090 // 'eval' is overwritten. Just call the function with the given arguments.
5091 receiver = Handle<Object>(Top::global_context()->global());
5092 }
5093 } else {
5094 // 'eval' is not bound in the global context. Just call the function
5095 // with the given arguments. This is not necessarily the global eval.
5096 if (receiver->IsContext()) {
5097 context = Handle<Context>::cast(receiver);
5098 receiver = Handle<Object>(context->get(index));
5099 }
5100 }
5101
5102 Handle<FixedArray> call = Factory::NewFixedArray(2);
5103 call->set(0, *callee);
5104 call->set(1, *receiver);
5105 return *call;
5106}
5107
5108
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005109static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5110 // This utility adjusts the property attributes for newly created Function
5111 // object ("new Function(...)") by changing the map.
5112 // All it does is changing the prototype property to enumerable
5113 // as specified in ECMA262, 15.3.5.2.
5114 HandleScope scope;
5115 ASSERT(args.length() == 1);
5116 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5117 ASSERT(func->map()->instance_type() ==
5118 Top::function_instance_map()->instance_type());
5119 ASSERT(func->map()->instance_size() ==
5120 Top::function_instance_map()->instance_size());
5121 func->set_map(*Top::function_instance_map());
5122 return *func;
5123}
5124
5125
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005126// Push an array unto an array of arrays if it is not already in the
5127// array. Returns true if the element was pushed on the stack and
5128// false otherwise.
5129static Object* Runtime_PushIfAbsent(Arguments args) {
5130 ASSERT(args.length() == 2);
5131 CONVERT_CHECKED(JSArray, array, args[0]);
5132 CONVERT_CHECKED(JSArray, element, args[1]);
5133 RUNTIME_ASSERT(array->HasFastElements());
5134 int length = Smi::cast(array->length())->value();
5135 FixedArray* elements = FixedArray::cast(array->elements());
5136 for (int i = 0; i < length; i++) {
5137 if (elements->get(i) == element) return Heap::false_value();
5138 }
5139 Object* obj = array->SetFastElement(length, element);
5140 if (obj->IsFailure()) return obj;
5141 return Heap::true_value();
5142}
5143
5144
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005145/**
5146 * A simple visitor visits every element of Array's.
5147 * The backend storage can be a fixed array for fast elements case,
5148 * or a dictionary for sparse array. Since Dictionary is a subtype
5149 * of FixedArray, the class can be used by both fast and slow cases.
5150 * The second parameter of the constructor, fast_elements, specifies
5151 * whether the storage is a FixedArray or Dictionary.
5152 *
5153 * An index limit is used to deal with the situation that a result array
5154 * length overflows 32-bit non-negative integer.
5155 */
5156class ArrayConcatVisitor {
5157 public:
5158 ArrayConcatVisitor(Handle<FixedArray> storage,
5159 uint32_t index_limit,
5160 bool fast_elements) :
5161 storage_(storage), index_limit_(index_limit),
5162 fast_elements_(fast_elements), index_offset_(0) { }
5163
5164 void visit(uint32_t i, Handle<Object> elm) {
5165 uint32_t index = i + index_offset_;
5166 if (index >= index_limit_) return;
5167
5168 if (fast_elements_) {
5169 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5170 storage_->set(index, *elm);
5171
5172 } else {
5173 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5174 Handle<Dictionary> result =
5175 Factory::DictionaryAtNumberPut(dict, index, elm);
5176 if (!result.is_identical_to(dict))
5177 storage_ = result;
5178 }
5179 }
5180
5181 void increase_index_offset(uint32_t delta) {
5182 index_offset_ += delta;
5183 }
5184
5185 private:
5186 Handle<FixedArray> storage_;
5187 uint32_t index_limit_;
5188 bool fast_elements_;
5189 uint32_t index_offset_;
5190};
5191
5192
5193/**
5194 * A helper function that visits elements of a JSObject. Only elements
5195 * whose index between 0 and range (exclusive) are visited.
5196 *
5197 * If the third parameter, visitor, is not NULL, the visitor is called
5198 * with parameters, 'visitor_index_offset + element index' and the element.
5199 *
5200 * It returns the number of visisted elements.
5201 */
5202static uint32_t IterateElements(Handle<JSObject> receiver,
5203 uint32_t range,
5204 ArrayConcatVisitor* visitor) {
5205 uint32_t num_of_elements = 0;
5206
5207 if (receiver->HasFastElements()) {
5208 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5209 uint32_t len = elements->length();
5210 if (range < len) len = range;
5211
5212 for (uint32_t j = 0; j < len; j++) {
5213 Handle<Object> e(elements->get(j));
5214 if (!e->IsTheHole()) {
5215 num_of_elements++;
5216 if (visitor)
5217 visitor->visit(j, e);
5218 }
5219 }
5220
5221 } else {
5222 Handle<Dictionary> dict(receiver->element_dictionary());
5223 uint32_t capacity = dict->Capacity();
5224 for (uint32_t j = 0; j < capacity; j++) {
5225 Handle<Object> k(dict->KeyAt(j));
5226 if (dict->IsKey(*k)) {
5227 ASSERT(k->IsNumber());
5228 uint32_t index = static_cast<uint32_t>(k->Number());
5229 if (index < range) {
5230 num_of_elements++;
5231 if (visitor) {
5232 visitor->visit(index,
5233 Handle<Object>(dict->ValueAt(j)));
5234 }
5235 }
5236 }
5237 }
5238 }
5239
5240 return num_of_elements;
5241}
5242
5243
5244/**
5245 * A helper function that visits elements of an Array object, and elements
5246 * on its prototypes.
5247 *
5248 * Elements on prototypes are visited first, and only elements whose indices
5249 * less than Array length are visited.
5250 *
5251 * If a ArrayConcatVisitor object is given, the visitor is called with
5252 * parameters, element's index + visitor_index_offset and the element.
5253 */
5254static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5255 ArrayConcatVisitor* visitor) {
5256 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5257 Handle<Object> obj = array;
5258
5259 static const int kEstimatedPrototypes = 3;
5260 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5261
5262 // Visit prototype first. If an element on the prototype is shadowed by
5263 // the inheritor using the same index, the ArrayConcatVisitor visits
5264 // the prototype element before the shadowing element.
5265 // The visitor can simply overwrite the old value by new value using
5266 // the same index. This follows Array::concat semantics.
5267 while (!obj->IsNull()) {
5268 objects.Add(Handle<JSObject>::cast(obj));
5269 obj = Handle<Object>(obj->GetPrototype());
5270 }
5271
5272 uint32_t nof_elements = 0;
5273 for (int i = objects.length() - 1; i >= 0; i--) {
5274 Handle<JSObject> obj = objects[i];
5275 nof_elements +=
5276 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5277 }
5278
5279 return nof_elements;
5280}
5281
5282
5283/**
5284 * A helper function of Runtime_ArrayConcat.
5285 *
5286 * The first argument is an Array of arrays and objects. It is the
5287 * same as the arguments array of Array::concat JS function.
5288 *
5289 * If an argument is an Array object, the function visits array
5290 * elements. If an argument is not an Array object, the function
5291 * visits the object as if it is an one-element array.
5292 *
5293 * If the result array index overflows 32-bit integer, the rounded
5294 * non-negative number is used as new length. For example, if one
5295 * array length is 2^32 - 1, second array length is 1, the
5296 * concatenated array length is 0.
5297 */
5298static uint32_t IterateArguments(Handle<JSArray> arguments,
5299 ArrayConcatVisitor* visitor) {
5300 uint32_t visited_elements = 0;
5301 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5302
5303 for (uint32_t i = 0; i < num_of_args; i++) {
5304 Handle<Object> obj(arguments->GetElement(i));
5305 if (obj->IsJSArray()) {
5306 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5307 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5308 uint32_t nof_elements =
5309 IterateArrayAndPrototypeElements(array, visitor);
5310 // Total elements of array and its prototype chain can be more than
5311 // the array length, but ArrayConcat can only concatenate at most
5312 // the array length number of elements.
5313 visited_elements += (nof_elements > len) ? len : nof_elements;
5314 if (visitor) visitor->increase_index_offset(len);
5315
5316 } else {
5317 if (visitor) {
5318 visitor->visit(0, obj);
5319 visitor->increase_index_offset(1);
5320 }
5321 visited_elements++;
5322 }
5323 }
5324 return visited_elements;
5325}
5326
5327
5328/**
5329 * Array::concat implementation.
5330 * See ECMAScript 262, 15.4.4.4.
5331 */
5332static Object* Runtime_ArrayConcat(Arguments args) {
5333 ASSERT(args.length() == 1);
5334 HandleScope handle_scope;
5335
5336 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5337 Handle<JSArray> arguments(arg_arrays);
5338
5339 // Pass 1: estimate the number of elements of the result
5340 // (it could be more than real numbers if prototype has elements).
5341 uint32_t result_length = 0;
5342 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5343
5344 { AssertNoAllocation nogc;
5345 for (uint32_t i = 0; i < num_of_args; i++) {
5346 Object* obj = arguments->GetElement(i);
5347 if (obj->IsJSArray()) {
5348 result_length +=
5349 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5350 } else {
5351 result_length++;
5352 }
5353 }
5354 }
5355
5356 // Allocate an empty array, will set length and content later.
5357 Handle<JSArray> result = Factory::NewJSArray(0);
5358
5359 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5360 // If estimated number of elements is more than half of length, a
5361 // fixed array (fast case) is more time and space-efficient than a
5362 // dictionary.
5363 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5364
5365 Handle<FixedArray> storage;
5366 if (fast_case) {
5367 // The backing storage array must have non-existing elements to
5368 // preserve holes across concat operations.
5369 storage = Factory::NewFixedArrayWithHoles(result_length);
5370
5371 } else {
5372 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5373 uint32_t at_least_space_for = estimate_nof_elements +
5374 (estimate_nof_elements >> 2);
5375 storage = Handle<FixedArray>::cast(
5376 Factory::NewDictionary(at_least_space_for));
5377 }
5378
5379 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5380
5381 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5382
5383 IterateArguments(arguments, &visitor);
5384
5385 result->set_length(*len);
5386 result->set_elements(*storage);
5387
5388 return *result;
5389}
5390
5391
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005392// This will not allocate (flatten the string), but it may run
5393// very slowly for very deeply nested ConsStrings. For debugging use only.
5394static Object* Runtime_GlobalPrint(Arguments args) {
5395 NoHandleAllocation ha;
5396 ASSERT(args.length() == 1);
5397
5398 CONVERT_CHECKED(String, string, args[0]);
5399 StringInputBuffer buffer(string);
5400 while (buffer.has_more()) {
5401 uint16_t character = buffer.GetNext();
5402 PrintF("%c", character);
5403 }
5404 return string;
5405}
5406
ager@chromium.org5ec48922009-05-05 07:25:34 +00005407// Moves all own elements of an object, that are below a limit, to positions
5408// starting at zero. All undefined values are placed after non-undefined values,
5409// and are followed by non-existing element. Does not change the length
5410// property.
5411// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005412static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005413 ASSERT(args.length() == 2);
5414 CONVERT_CHECKED(JSObject, object, args[0]);
5415 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5416 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005417}
5418
5419
5420// Move contents of argument 0 (an array) to argument 1 (an array)
5421static Object* Runtime_MoveArrayContents(Arguments args) {
5422 ASSERT(args.length() == 2);
5423 CONVERT_CHECKED(JSArray, from, args[0]);
5424 CONVERT_CHECKED(JSArray, to, args[1]);
5425 to->SetContent(FixedArray::cast(from->elements()));
5426 to->set_length(from->length());
5427 from->SetContent(Heap::empty_fixed_array());
5428 from->set_length(0);
5429 return to;
5430}
5431
5432
5433// How many elements does this array have?
5434static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5435 ASSERT(args.length() == 1);
5436 CONVERT_CHECKED(JSArray, array, args[0]);
5437 HeapObject* elements = array->elements();
5438 if (elements->IsDictionary()) {
5439 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5440 } else {
5441 return array->length();
5442 }
5443}
5444
5445
5446// Returns an array that tells you where in the [0, length) interval an array
5447// might have elements. Can either return keys or intervals. Keys can have
5448// gaps in (undefined). Intervals can also span over some undefined keys.
5449static Object* Runtime_GetArrayKeys(Arguments args) {
5450 ASSERT(args.length() == 2);
5451 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005452 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005453 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005454 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005455 // Create an array and get all the keys into it, then remove all the
5456 // keys that are not integers in the range 0 to length-1.
5457 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5458 int keys_length = keys->length();
5459 for (int i = 0; i < keys_length; i++) {
5460 Object* key = keys->get(i);
5461 uint32_t index;
5462 if (!Array::IndexFromObject(key, &index) || index >= length) {
5463 // Zap invalid keys.
5464 keys->set_undefined(i);
5465 }
5466 }
5467 return *Factory::NewJSArrayWithElements(keys);
5468 } else {
5469 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5470 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005471 single_interval->set(0,
5472 Smi::FromInt(-1),
5473 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005474 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5475 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005476 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005477 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005478 single_interval->set(1, *length_object);
5479 return *Factory::NewJSArrayWithElements(single_interval);
5480 }
5481}
5482
5483
5484// DefineAccessor takes an optional final argument which is the
5485// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5486// to the way accessors are implemented, it is set for both the getter
5487// and setter on the first call to DefineAccessor and ignored on
5488// subsequent calls.
5489static Object* Runtime_DefineAccessor(Arguments args) {
5490 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5491 // Compute attributes.
5492 PropertyAttributes attributes = NONE;
5493 if (args.length() == 5) {
5494 CONVERT_CHECKED(Smi, attrs, args[4]);
5495 int value = attrs->value();
5496 // Only attribute bits should be set.
5497 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5498 attributes = static_cast<PropertyAttributes>(value);
5499 }
5500
5501 CONVERT_CHECKED(JSObject, obj, args[0]);
5502 CONVERT_CHECKED(String, name, args[1]);
5503 CONVERT_CHECKED(Smi, flag, args[2]);
5504 CONVERT_CHECKED(JSFunction, fun, args[3]);
5505 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5506}
5507
5508
5509static Object* Runtime_LookupAccessor(Arguments args) {
5510 ASSERT(args.length() == 3);
5511 CONVERT_CHECKED(JSObject, obj, args[0]);
5512 CONVERT_CHECKED(String, name, args[1]);
5513 CONVERT_CHECKED(Smi, flag, args[2]);
5514 return obj->LookupAccessor(name, flag->value() == 0);
5515}
5516
5517
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005518#ifdef ENABLE_DEBUGGER_SUPPORT
5519static Object* Runtime_DebugBreak(Arguments args) {
5520 ASSERT(args.length() == 0);
5521 return Execution::DebugBreakHelper();
5522}
5523
5524
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005525// Helper functions for wrapping and unwrapping stack frame ids.
5526static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005527 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005528 return Smi::FromInt(id >> 2);
5529}
5530
5531
5532static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5533 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5534}
5535
5536
5537// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005538// args[0]: debug event listener function to set or null or undefined for
5539// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005541static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005543 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5544 args[0]->IsUndefined() ||
5545 args[0]->IsNull());
5546 Handle<Object> callback = args.at<Object>(0);
5547 Handle<Object> data = args.at<Object>(1);
5548 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005549
5550 return Heap::undefined_value();
5551}
5552
5553
5554static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005555 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005556 StackGuard::DebugBreak();
5557 return Heap::undefined_value();
5558}
5559
5560
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005561// Find the length of the prototype chain that is to to handled as one. If a
5562// prototype object is hidden it is to be viewed as part of the the object it
5563// is prototype for.
5564static int LocalPrototypeChainLength(JSObject* obj) {
5565 int count = 1;
5566 Object* proto = obj->GetPrototype();
5567 while (proto->IsJSObject() &&
5568 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5569 count++;
5570 proto = JSObject::cast(proto)->GetPrototype();
5571 }
5572 return count;
5573}
5574
5575
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005576static Object* DebugLookupResultValue(Object* receiver, String* name,
5577 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005578 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005579 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005580 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005581 case NORMAL: {
5582 Dictionary* dict =
5583 JSObject::cast(result->holder())->property_dictionary();
5584 value = dict->ValueAt(result->GetDictionaryEntry());
5585 if (value->IsTheHole()) {
5586 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005587 }
5588 return value;
5589 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005590 case FIELD:
5591 value =
5592 JSObject::cast(
5593 result->holder())->FastPropertyAt(result->GetFieldIndex());
5594 if (value->IsTheHole()) {
5595 return Heap::undefined_value();
5596 }
5597 return value;
5598 case CONSTANT_FUNCTION:
5599 return result->GetConstantFunction();
5600 case CALLBACKS: {
5601 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005602 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005603 value = receiver->GetPropertyWithCallback(
5604 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005605 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005606 value = Top::pending_exception();
5607 Top::clear_pending_exception();
5608 if (caught_exception != NULL) {
5609 *caught_exception = true;
5610 }
5611 }
5612 return value;
5613 } else {
5614 return Heap::undefined_value();
5615 }
5616 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005617 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005618 case MAP_TRANSITION:
5619 case CONSTANT_TRANSITION:
5620 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005621 return Heap::undefined_value();
5622 default:
5623 UNREACHABLE();
5624 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005625 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005626 return Heap::undefined_value();
5627}
5628
5629
ager@chromium.org32912102009-01-16 10:38:43 +00005630// Get debugger related details for an object property.
5631// args[0]: object holding property
5632// args[1]: name of the property
5633//
5634// The array returned contains the following information:
5635// 0: Property value
5636// 1: Property details
5637// 2: Property value is exception
5638// 3: Getter function if defined
5639// 4: Setter function if defined
5640// Items 2-4 are only filled if the property has either a getter or a setter
5641// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005642static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005643 HandleScope scope;
5644
5645 ASSERT(args.length() == 2);
5646
5647 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5648 CONVERT_ARG_CHECKED(String, name, 1);
5649
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005650 // Make sure to set the current context to the context before the debugger was
5651 // entered (if the debugger is entered). The reason for switching context here
5652 // is that for some property lookups (accessors and interceptors) callbacks
5653 // into the embedding application can occour, and the embedding application
5654 // could have the assumption that its own global context is the current
5655 // context and not some internal debugger context.
5656 SaveContext save;
5657 if (Debug::InDebugger()) {
5658 Top::set_context(*Debug::debugger_entry()->GetContext());
5659 }
5660
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005661 // Skip the global proxy as it has no properties and always delegates to the
5662 // real global object.
5663 if (obj->IsJSGlobalProxy()) {
5664 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5665 }
5666
5667
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005668 // Check if the name is trivially convertible to an index and get the element
5669 // if so.
5670 uint32_t index;
5671 if (name->AsArrayIndex(&index)) {
5672 Handle<FixedArray> details = Factory::NewFixedArray(2);
5673 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5674 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5675 return *Factory::NewJSArrayWithElements(details);
5676 }
5677
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005678 // Find the number of objects making up this.
5679 int length = LocalPrototypeChainLength(*obj);
5680
5681 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005682 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005683 Handle<JSObject> jsproto = obj;
5684 for (int i = 0; i < length; i++) {
5685 jsproto->LocalLookup(*name, &result);
5686 if (result.IsProperty()) {
5687 break;
5688 }
5689 if (i < length - 1) {
5690 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5691 }
5692 }
5693
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005694 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005695 // LookupResult is not GC safe as all its members are raw object pointers.
5696 // When calling DebugLookupResultValue GC can happen as this might invoke
5697 // callbacks. After the call to DebugLookupResultValue the callback object
5698 // in the LookupResult might still be needed. Put it into a handle for later
5699 // use.
5700 PropertyType result_type = result.type();
5701 Handle<Object> result_callback_obj;
5702 if (result_type == CALLBACKS) {
5703 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5704 }
5705
5706 // Find the actual value. Don't use result after this call as it's content
5707 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005708 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005709 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005710 &caught_exception);
5711 if (value->IsFailure()) return value;
5712 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005713
ager@chromium.org32912102009-01-16 10:38:43 +00005714 // If the callback object is a fixed array then it contains JavaScript
5715 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005716 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5717 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005718 Handle<FixedArray> details =
5719 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005720 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005721 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005722 if (hasJavaScriptAccessors) {
5723 details->set(2,
5724 caught_exception ? Heap::true_value() : Heap::false_value());
5725 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5726 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5727 }
5728
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005729 return *Factory::NewJSArrayWithElements(details);
5730 }
5731 return Heap::undefined_value();
5732}
5733
5734
5735static Object* Runtime_DebugGetProperty(Arguments args) {
5736 HandleScope scope;
5737
5738 ASSERT(args.length() == 2);
5739
5740 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5741 CONVERT_ARG_CHECKED(String, name, 1);
5742
5743 LookupResult result;
5744 obj->Lookup(*name, &result);
5745 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005746 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005747 }
5748 return Heap::undefined_value();
5749}
5750
5751
5752// Return the names of the local named properties.
5753// args[0]: object
5754static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5755 HandleScope scope;
5756 ASSERT(args.length() == 1);
5757 if (!args[0]->IsJSObject()) {
5758 return Heap::undefined_value();
5759 }
5760 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5761
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005762 // Skip the global proxy as it has no properties and always delegates to the
5763 // real global object.
5764 if (obj->IsJSGlobalProxy()) {
5765 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5766 }
5767
5768 // Find the number of objects making up this.
5769 int length = LocalPrototypeChainLength(*obj);
5770
5771 // Find the number of local properties for each of the objects.
5772 int* local_property_count = NewArray<int>(length);
5773 int total_property_count = 0;
5774 Handle<JSObject> jsproto = obj;
5775 for (int i = 0; i < length; i++) {
5776 int n;
5777 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5778 local_property_count[i] = n;
5779 total_property_count += n;
5780 if (i < length - 1) {
5781 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5782 }
5783 }
5784
5785 // Allocate an array with storage for all the property names.
5786 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5787
5788 // Get the property names.
5789 jsproto = obj;
5790 for (int i = 0; i < length; i++) {
5791 jsproto->GetLocalPropertyNames(*names,
5792 i == 0 ? 0 : local_property_count[i - 1]);
5793 if (i < length - 1) {
5794 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5795 }
5796 }
5797
5798 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005799 return *Factory::NewJSArrayWithElements(names);
5800}
5801
5802
5803// Return the names of the local indexed properties.
5804// args[0]: object
5805static Object* Runtime_DebugLocalElementNames(Arguments args) {
5806 HandleScope scope;
5807 ASSERT(args.length() == 1);
5808 if (!args[0]->IsJSObject()) {
5809 return Heap::undefined_value();
5810 }
5811 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5812
5813 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5814 Handle<FixedArray> names = Factory::NewFixedArray(n);
5815 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5816 return *Factory::NewJSArrayWithElements(names);
5817}
5818
5819
5820// Return the property type calculated from the property details.
5821// args[0]: smi with property details.
5822static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5823 ASSERT(args.length() == 1);
5824 CONVERT_CHECKED(Smi, details, args[0]);
5825 PropertyType type = PropertyDetails(details).type();
5826 return Smi::FromInt(static_cast<int>(type));
5827}
5828
5829
5830// Return the property attribute calculated from the property details.
5831// args[0]: smi with property details.
5832static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5833 ASSERT(args.length() == 1);
5834 CONVERT_CHECKED(Smi, details, args[0]);
5835 PropertyAttributes attributes = PropertyDetails(details).attributes();
5836 return Smi::FromInt(static_cast<int>(attributes));
5837}
5838
5839
5840// Return the property insertion index calculated from the property details.
5841// args[0]: smi with property details.
5842static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5843 ASSERT(args.length() == 1);
5844 CONVERT_CHECKED(Smi, details, args[0]);
5845 int index = PropertyDetails(details).index();
5846 return Smi::FromInt(index);
5847}
5848
5849
5850// Return information on whether an object has a named or indexed interceptor.
5851// args[0]: object
5852static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5853 HandleScope scope;
5854 ASSERT(args.length() == 1);
5855 if (!args[0]->IsJSObject()) {
5856 return Smi::FromInt(0);
5857 }
5858 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5859
5860 int result = 0;
5861 if (obj->HasNamedInterceptor()) result |= 2;
5862 if (obj->HasIndexedInterceptor()) result |= 1;
5863
5864 return Smi::FromInt(result);
5865}
5866
5867
5868// Return property names from named interceptor.
5869// args[0]: object
5870static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5871 HandleScope scope;
5872 ASSERT(args.length() == 1);
5873 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005874
ager@chromium.org32912102009-01-16 10:38:43 +00005875 if (obj->HasNamedInterceptor()) {
5876 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5877 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5878 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005879 return Heap::undefined_value();
5880}
5881
5882
5883// Return element names from indexed interceptor.
5884// args[0]: object
5885static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5886 HandleScope scope;
5887 ASSERT(args.length() == 1);
5888 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005889
ager@chromium.org32912102009-01-16 10:38:43 +00005890 if (obj->HasIndexedInterceptor()) {
5891 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5892 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5893 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894 return Heap::undefined_value();
5895}
5896
5897
5898// Return property value from named interceptor.
5899// args[0]: object
5900// args[1]: property name
5901static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5902 HandleScope scope;
5903 ASSERT(args.length() == 2);
5904 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5905 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5906 CONVERT_ARG_CHECKED(String, name, 1);
5907
5908 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005909 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005910}
5911
5912
5913// Return element value from indexed interceptor.
5914// args[0]: object
5915// args[1]: index
5916static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5917 HandleScope scope;
5918 ASSERT(args.length() == 2);
5919 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5920 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5921 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5922
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005923 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005924}
5925
5926
5927static Object* Runtime_CheckExecutionState(Arguments args) {
5928 ASSERT(args.length() >= 1);
5929 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005930 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005931 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005932 return Top::Throw(Heap::illegal_execution_state_symbol());
5933 }
5934
5935 return Heap::true_value();
5936}
5937
5938
5939static Object* Runtime_GetFrameCount(Arguments args) {
5940 HandleScope scope;
5941 ASSERT(args.length() == 1);
5942
5943 // Check arguments.
5944 Object* result = Runtime_CheckExecutionState(args);
5945 if (result->IsFailure()) return result;
5946
5947 // Count all frames which are relevant to debugging stack trace.
5948 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005949 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005950 if (id == StackFrame::NO_ID) {
5951 // If there is no JavaScript stack frame count is 0.
5952 return Smi::FromInt(0);
5953 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005954 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5955 return Smi::FromInt(n);
5956}
5957
5958
5959static const int kFrameDetailsFrameIdIndex = 0;
5960static const int kFrameDetailsReceiverIndex = 1;
5961static const int kFrameDetailsFunctionIndex = 2;
5962static const int kFrameDetailsArgumentCountIndex = 3;
5963static const int kFrameDetailsLocalCountIndex = 4;
5964static const int kFrameDetailsSourcePositionIndex = 5;
5965static const int kFrameDetailsConstructCallIndex = 6;
5966static const int kFrameDetailsDebuggerFrameIndex = 7;
5967static const int kFrameDetailsFirstDynamicIndex = 8;
5968
5969// Return an array with frame details
5970// args[0]: number: break id
5971// args[1]: number: frame index
5972//
5973// The array returned contains the following information:
5974// 0: Frame id
5975// 1: Receiver
5976// 2: Function
5977// 3: Argument count
5978// 4: Local count
5979// 5: Source position
5980// 6: Constructor call
5981// 7: Debugger frame
5982// Arguments name, value
5983// Locals name, value
5984static Object* Runtime_GetFrameDetails(Arguments args) {
5985 HandleScope scope;
5986 ASSERT(args.length() == 2);
5987
5988 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005989 Object* check = Runtime_CheckExecutionState(args);
5990 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005991 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5992
5993 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005994 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005995 if (id == StackFrame::NO_ID) {
5996 // If there are no JavaScript stack frames return undefined.
5997 return Heap::undefined_value();
5998 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005999 int count = 0;
6000 JavaScriptFrameIterator it(id);
6001 for (; !it.done(); it.Advance()) {
6002 if (count == index) break;
6003 count++;
6004 }
6005 if (it.done()) return Heap::undefined_value();
6006
6007 // Traverse the saved contexts chain to find the active context for the
6008 // selected frame.
6009 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006010 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006011 save = save->prev();
6012 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006013 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006014
6015 // Get the frame id.
6016 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6017
6018 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006019 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006020
6021 // Check for constructor frame.
6022 bool constructor = it.frame()->IsConstructor();
6023
6024 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006025 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006026 ScopeInfo<> info(*code);
6027
6028 // Get the context.
6029 Handle<Context> context(Context::cast(it.frame()->context()));
6030
6031 // Get the locals names and values into a temporary array.
6032 //
6033 // TODO(1240907): Hide compiler-introduced stack variables
6034 // (e.g. .result)? For users of the debugger, they will probably be
6035 // confusing.
6036 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6037 for (int i = 0; i < info.NumberOfLocals(); i++) {
6038 // Name of the local.
6039 locals->set(i * 2, *info.LocalName(i));
6040
6041 // Fetch the value of the local - either from the stack or from a
6042 // heap-allocated context.
6043 if (i < info.number_of_stack_slots()) {
6044 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6045 } else {
6046 Handle<String> name = info.LocalName(i);
6047 // Traverse the context chain to the function context as all local
6048 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006049 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006050 context = Handle<Context>(context->previous());
6051 }
6052 ASSERT(context->is_function_context());
6053 locals->set(i * 2 + 1,
6054 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6055 NULL)));
6056 }
6057 }
6058
6059 // Now advance to the arguments adapter frame (if any). If contains all
6060 // the provided parameters and
6061
6062 // Now advance to the arguments adapter frame (if any). It contains all
6063 // the provided parameters whereas the function frame always have the number
6064 // of arguments matching the functions parameters. The rest of the
6065 // information (except for what is collected above) is the same.
6066 it.AdvanceToArgumentsFrame();
6067
6068 // Find the number of arguments to fill. At least fill the number of
6069 // parameters for the function and fill more if more parameters are provided.
6070 int argument_count = info.number_of_parameters();
6071 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6072 argument_count = it.frame()->GetProvidedParametersCount();
6073 }
6074
6075 // Calculate the size of the result.
6076 int details_size = kFrameDetailsFirstDynamicIndex +
6077 2 * (argument_count + info.NumberOfLocals());
6078 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6079
6080 // Add the frame id.
6081 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6082
6083 // Add the function (same as in function frame).
6084 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6085
6086 // Add the arguments count.
6087 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6088
6089 // Add the locals count
6090 details->set(kFrameDetailsLocalCountIndex,
6091 Smi::FromInt(info.NumberOfLocals()));
6092
6093 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006094 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006095 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6096 } else {
6097 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6098 }
6099
6100 // Add the constructor information.
6101 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6102
6103 // Add information on whether this frame is invoked in the debugger context.
6104 details->set(kFrameDetailsDebuggerFrameIndex,
6105 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6106
6107 // Fill the dynamic part.
6108 int details_index = kFrameDetailsFirstDynamicIndex;
6109
6110 // Add arguments name and value.
6111 for (int i = 0; i < argument_count; i++) {
6112 // Name of the argument.
6113 if (i < info.number_of_parameters()) {
6114 details->set(details_index++, *info.parameter_name(i));
6115 } else {
6116 details->set(details_index++, Heap::undefined_value());
6117 }
6118
6119 // Parameter value.
6120 if (i < it.frame()->GetProvidedParametersCount()) {
6121 details->set(details_index++, it.frame()->GetParameter(i));
6122 } else {
6123 details->set(details_index++, Heap::undefined_value());
6124 }
6125 }
6126
6127 // Add locals name and value from the temporary copy from the function frame.
6128 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6129 details->set(details_index++, locals->get(i));
6130 }
6131
6132 // Add the receiver (same as in function frame).
6133 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6134 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6135 Handle<Object> receiver(it.frame()->receiver());
6136 if (!receiver->IsJSObject()) {
6137 // If the receiver is NOT a JSObject we have hit an optimization
6138 // where a value object is not converted into a wrapped JS objects.
6139 // To hide this optimization from the debugger, we wrap the receiver
6140 // by creating correct wrapper object based on the calling frame's
6141 // global context.
6142 it.Advance();
6143 Handle<Context> calling_frames_global_context(
6144 Context::cast(Context::cast(it.frame()->context())->global_context()));
6145 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6146 }
6147 details->set(kFrameDetailsReceiverIndex, *receiver);
6148
6149 ASSERT_EQ(details_size, details_index);
6150 return *Factory::NewJSArrayWithElements(details);
6151}
6152
6153
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006154// Copy all the context locals into an object used to materialize a scope.
6155static void CopyContextLocalsToScopeObject(Handle<Code> code,
6156 ScopeInfo<>& scope_info,
6157 Handle<Context> context,
6158 Handle<JSObject> scope_object) {
6159 // Fill all context locals to the context extension.
6160 for (int i = Context::MIN_CONTEXT_SLOTS;
6161 i < scope_info.number_of_context_slots();
6162 i++) {
6163 int context_index =
6164 ScopeInfo<>::ContextSlotIndex(*code,
6165 *scope_info.context_slot_name(i),
6166 NULL);
6167
6168 // Don't include the arguments shadow (.arguments) context variable.
6169 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6170 SetProperty(scope_object,
6171 scope_info.context_slot_name(i),
6172 Handle<Object>(context->get(context_index)), NONE);
6173 }
6174 }
6175}
6176
6177
6178// Create a plain JSObject which materializes the local scope for the specified
6179// frame.
6180static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6181 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6182 Handle<Code> code(function->code());
6183 ScopeInfo<> scope_info(*code);
6184
6185 // Allocate and initialize a JSObject with all the arguments, stack locals
6186 // heap locals and extension properties of the debugged function.
6187 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6188
6189 // First fill all parameters.
6190 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6191 SetProperty(local_scope,
6192 scope_info.parameter_name(i),
6193 Handle<Object>(frame->GetParameter(i)), NONE);
6194 }
6195
6196 // Second fill all stack locals.
6197 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6198 SetProperty(local_scope,
6199 scope_info.stack_slot_name(i),
6200 Handle<Object>(frame->GetExpression(i)), NONE);
6201 }
6202
6203 // Third fill all context locals.
6204 Handle<Context> frame_context(Context::cast(frame->context()));
6205 Handle<Context> function_context(frame_context->fcontext());
6206 CopyContextLocalsToScopeObject(code, scope_info,
6207 function_context, local_scope);
6208
6209 // Finally copy any properties from the function context extension. This will
6210 // be variables introduced by eval.
6211 if (function_context->closure() == *function) {
6212 if (function_context->has_extension() &&
6213 !function_context->IsGlobalContext()) {
6214 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6215 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6216 for (int i = 0; i < keys->length(); i++) {
6217 // Names of variables introduced by eval are strings.
6218 ASSERT(keys->get(i)->IsString());
6219 Handle<String> key(String::cast(keys->get(i)));
6220 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6221 }
6222 }
6223 }
6224 return local_scope;
6225}
6226
6227
6228// Create a plain JSObject which materializes the closure content for the
6229// context.
6230static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6231 ASSERT(context->is_function_context());
6232
6233 Handle<Code> code(context->closure()->code());
6234 ScopeInfo<> scope_info(*code);
6235
6236 // Allocate and initialize a JSObject with all the content of theis function
6237 // closure.
6238 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6239
6240 // Check whether the arguments shadow object exists.
6241 int arguments_shadow_index =
6242 ScopeInfo<>::ContextSlotIndex(*code,
6243 Heap::arguments_shadow_symbol(),
6244 NULL);
6245 if (arguments_shadow_index >= 0) {
6246 // In this case all the arguments are available in the arguments shadow
6247 // object.
6248 Handle<JSObject> arguments_shadow(
6249 JSObject::cast(context->get(arguments_shadow_index)));
6250 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6251 SetProperty(closure_scope,
6252 scope_info.parameter_name(i),
6253 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6254 }
6255 }
6256
6257 // Fill all context locals to the context extension.
6258 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6259
6260 // Finally copy any properties from the function context extension. This will
6261 // be variables introduced by eval.
6262 if (context->has_extension()) {
6263 Handle<JSObject> ext(JSObject::cast(context->extension()));
6264 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6265 for (int i = 0; i < keys->length(); i++) {
6266 // Names of variables introduced by eval are strings.
6267 ASSERT(keys->get(i)->IsString());
6268 Handle<String> key(String::cast(keys->get(i)));
6269 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6270 }
6271 }
6272
6273 return closure_scope;
6274}
6275
6276
6277// Iterate over the actual scopes visible from a stack frame. All scopes are
6278// backed by an actual context except the local scope, which is inserted
6279// "artifically" in the context chain.
6280class ScopeIterator {
6281 public:
6282 enum ScopeType {
6283 ScopeTypeGlobal = 0,
6284 ScopeTypeLocal,
6285 ScopeTypeWith,
6286 ScopeTypeClosure
6287 };
6288
6289 explicit ScopeIterator(JavaScriptFrame* frame)
6290 : frame_(frame),
6291 function_(JSFunction::cast(frame->function())),
6292 context_(Context::cast(frame->context())),
6293 local_done_(false),
6294 at_local_(false) {
6295
6296 // Check whether the first scope is actually a local scope.
6297 if (context_->IsGlobalContext()) {
6298 // If there is a stack slot for .result then this local scope has been
6299 // created for evaluating top level code and it is not a real local scope.
6300 // Checking for the existence of .result seems fragile, but the scope info
6301 // saved with the code object does not otherwise have that information.
6302 Handle<Code> code(function_->code());
6303 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6304 at_local_ = index < 0;
6305 } else if (context_->is_function_context()) {
6306 at_local_ = true;
6307 }
6308 }
6309
6310 // More scopes?
6311 bool Done() { return context_.is_null(); }
6312
6313 // Move to the next scope.
6314 void Next() {
6315 // If at a local scope mark the local scope as passed.
6316 if (at_local_) {
6317 at_local_ = false;
6318 local_done_ = true;
6319
6320 // If the current context is not associated with the local scope the
6321 // current context is the next real scope, so don't move to the next
6322 // context in this case.
6323 if (context_->closure() != *function_) {
6324 return;
6325 }
6326 }
6327
6328 // The global scope is always the last in the chain.
6329 if (context_->IsGlobalContext()) {
6330 context_ = Handle<Context>();
6331 return;
6332 }
6333
6334 // Move to the next context.
6335 if (context_->is_function_context()) {
6336 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6337 } else {
6338 context_ = Handle<Context>(context_->previous());
6339 }
6340
6341 // If passing the local scope indicate that the current scope is now the
6342 // local scope.
6343 if (!local_done_ &&
6344 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6345 at_local_ = true;
6346 }
6347 }
6348
6349 // Return the type of the current scope.
6350 int Type() {
6351 if (at_local_) {
6352 return ScopeTypeLocal;
6353 }
6354 if (context_->IsGlobalContext()) {
6355 ASSERT(context_->global()->IsGlobalObject());
6356 return ScopeTypeGlobal;
6357 }
6358 if (context_->is_function_context()) {
6359 return ScopeTypeClosure;
6360 }
6361 ASSERT(context_->has_extension());
6362 ASSERT(!context_->extension()->IsJSContextExtensionObject());
6363 return ScopeTypeWith;
6364 }
6365
6366 // Return the JavaScript object with the content of the current scope.
6367 Handle<JSObject> ScopeObject() {
6368 switch (Type()) {
6369 case ScopeIterator::ScopeTypeGlobal:
6370 return Handle<JSObject>(CurrentContext()->global());
6371 break;
6372 case ScopeIterator::ScopeTypeLocal:
6373 // Materialize the content of the local scope into a JSObject.
6374 return MaterializeLocalScope(frame_);
6375 break;
6376 case ScopeIterator::ScopeTypeWith:
6377 // Return the with object.
6378 return Handle<JSObject>(CurrentContext()->extension());
6379 break;
6380 case ScopeIterator::ScopeTypeClosure:
6381 // Materialize the content of the closure scope into a JSObject.
6382 return MaterializeClosure(CurrentContext());
6383 break;
6384 }
6385 UNREACHABLE();
6386 return Handle<JSObject>();
6387 }
6388
6389 // Return the context for this scope. For the local context there might not
6390 // be an actual context.
6391 Handle<Context> CurrentContext() {
6392 if (at_local_ && context_->closure() != *function_) {
6393 return Handle<Context>();
6394 }
6395 return context_;
6396 }
6397
6398#ifdef DEBUG
6399 // Debug print of the content of the current scope.
6400 void DebugPrint() {
6401 switch (Type()) {
6402 case ScopeIterator::ScopeTypeGlobal:
6403 PrintF("Global:\n");
6404 CurrentContext()->Print();
6405 break;
6406
6407 case ScopeIterator::ScopeTypeLocal: {
6408 PrintF("Local:\n");
6409 Handle<Code> code(function_->code());
6410 ScopeInfo<> scope_info(*code);
6411 scope_info.Print();
6412 if (!CurrentContext().is_null()) {
6413 CurrentContext()->Print();
6414 if (CurrentContext()->has_extension()) {
6415 Handle<JSObject> extension =
6416 Handle<JSObject>(CurrentContext()->extension());
6417 if (extension->IsJSContextExtensionObject()) {
6418 extension->Print();
6419 }
6420 }
6421 }
6422 break;
6423 }
6424
6425 case ScopeIterator::ScopeTypeWith: {
6426 PrintF("With:\n");
6427 Handle<JSObject> extension =
6428 Handle<JSObject>(CurrentContext()->extension());
6429 extension->Print();
6430 break;
6431 }
6432
6433 case ScopeIterator::ScopeTypeClosure: {
6434 PrintF("Closure:\n");
6435 CurrentContext()->Print();
6436 if (CurrentContext()->has_extension()) {
6437 Handle<JSObject> extension =
6438 Handle<JSObject>(CurrentContext()->extension());
6439 if (extension->IsJSContextExtensionObject()) {
6440 extension->Print();
6441 }
6442 }
6443 break;
6444 }
6445
6446 default:
6447 UNREACHABLE();
6448 }
6449 PrintF("\n");
6450 }
6451#endif
6452
6453 private:
6454 JavaScriptFrame* frame_;
6455 Handle<JSFunction> function_;
6456 Handle<Context> context_;
6457 bool local_done_;
6458 bool at_local_;
6459
6460 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6461};
6462
6463
6464static Object* Runtime_GetScopeCount(Arguments args) {
6465 HandleScope scope;
6466 ASSERT(args.length() == 2);
6467
6468 // Check arguments.
6469 Object* check = Runtime_CheckExecutionState(args);
6470 if (check->IsFailure()) return check;
6471 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6472
6473 // Get the frame where the debugging is performed.
6474 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6475 JavaScriptFrameIterator it(id);
6476 JavaScriptFrame* frame = it.frame();
6477
6478 // Count the visible scopes.
6479 int n = 0;
6480 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6481 n++;
6482 }
6483
6484 return Smi::FromInt(n);
6485}
6486
6487
6488static const int kScopeDetailsTypeIndex = 0;
6489static const int kScopeDetailsObjectIndex = 1;
6490static const int kScopeDetailsSize = 2;
6491
6492// Return an array with scope details
6493// args[0]: number: break id
6494// args[1]: number: frame index
6495// args[2]: number: scope index
6496//
6497// The array returned contains the following information:
6498// 0: Scope type
6499// 1: Scope object
6500static Object* Runtime_GetScopeDetails(Arguments args) {
6501 HandleScope scope;
6502 ASSERT(args.length() == 3);
6503
6504 // Check arguments.
6505 Object* check = Runtime_CheckExecutionState(args);
6506 if (check->IsFailure()) return check;
6507 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6508 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6509
6510 // Get the frame where the debugging is performed.
6511 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6512 JavaScriptFrameIterator frame_it(id);
6513 JavaScriptFrame* frame = frame_it.frame();
6514
6515 // Find the requested scope.
6516 int n = 0;
6517 ScopeIterator it(frame);
6518 for (; !it.Done() && n < index; it.Next()) {
6519 n++;
6520 }
6521 if (it.Done()) {
6522 return Heap::undefined_value();
6523 }
6524
6525 // Calculate the size of the result.
6526 int details_size = kScopeDetailsSize;
6527 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6528
6529 // Fill in scope details.
6530 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6531 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6532
6533 return *Factory::NewJSArrayWithElements(details);
6534}
6535
6536
6537static Object* Runtime_DebugPrintScopes(Arguments args) {
6538 HandleScope scope;
6539 ASSERT(args.length() == 0);
6540
6541#ifdef DEBUG
6542 // Print the scopes for the top frame.
6543 StackFrameLocator locator;
6544 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6545 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6546 it.DebugPrint();
6547 }
6548#endif
6549 return Heap::undefined_value();
6550}
6551
6552
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006553static Object* Runtime_GetCFrames(Arguments args) {
6554 HandleScope scope;
6555 ASSERT(args.length() == 1);
6556 Object* result = Runtime_CheckExecutionState(args);
6557 if (result->IsFailure()) return result;
6558
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006559#if V8_HOST_ARCH_64_BIT
6560 UNIMPLEMENTED();
6561 return Heap::undefined_value();
6562#else
6563
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006564 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006565 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6566 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006567 if (frames_count == OS::kStackWalkError) {
6568 return Heap::undefined_value();
6569 }
6570
6571 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6572 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6573 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6574 for (int i = 0; i < frames_count; i++) {
6575 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6576 frame_value->SetProperty(
6577 *address_str,
6578 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6579 NONE);
6580
6581 // Get the stack walk text for this frame.
6582 Handle<String> frame_text;
6583 if (strlen(frames[i].text) > 0) {
6584 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6585 frame_text = Factory::NewStringFromAscii(str);
6586 }
6587
6588 if (!frame_text.is_null()) {
6589 frame_value->SetProperty(*text_str, *frame_text, NONE);
6590 }
6591
6592 frames_array->set(i, *frame_value);
6593 }
6594 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006595#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006596}
6597
6598
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006599static Object* Runtime_GetThreadCount(Arguments args) {
6600 HandleScope scope;
6601 ASSERT(args.length() == 1);
6602
6603 // Check arguments.
6604 Object* result = Runtime_CheckExecutionState(args);
6605 if (result->IsFailure()) return result;
6606
6607 // Count all archived V8 threads.
6608 int n = 0;
6609 for (ThreadState* thread = ThreadState::FirstInUse();
6610 thread != NULL;
6611 thread = thread->Next()) {
6612 n++;
6613 }
6614
6615 // Total number of threads is current thread and archived threads.
6616 return Smi::FromInt(n + 1);
6617}
6618
6619
6620static const int kThreadDetailsCurrentThreadIndex = 0;
6621static const int kThreadDetailsThreadIdIndex = 1;
6622static const int kThreadDetailsSize = 2;
6623
6624// Return an array with thread details
6625// args[0]: number: break id
6626// args[1]: number: thread index
6627//
6628// The array returned contains the following information:
6629// 0: Is current thread?
6630// 1: Thread id
6631static Object* Runtime_GetThreadDetails(Arguments args) {
6632 HandleScope scope;
6633 ASSERT(args.length() == 2);
6634
6635 // Check arguments.
6636 Object* check = Runtime_CheckExecutionState(args);
6637 if (check->IsFailure()) return check;
6638 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6639
6640 // Allocate array for result.
6641 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6642
6643 // Thread index 0 is current thread.
6644 if (index == 0) {
6645 // Fill the details.
6646 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6647 details->set(kThreadDetailsThreadIdIndex,
6648 Smi::FromInt(ThreadManager::CurrentId()));
6649 } else {
6650 // Find the thread with the requested index.
6651 int n = 1;
6652 ThreadState* thread = ThreadState::FirstInUse();
6653 while (index != n && thread != NULL) {
6654 thread = thread->Next();
6655 n++;
6656 }
6657 if (thread == NULL) {
6658 return Heap::undefined_value();
6659 }
6660
6661 // Fill the details.
6662 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6663 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6664 }
6665
6666 // Convert to JS array and return.
6667 return *Factory::NewJSArrayWithElements(details);
6668}
6669
6670
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006671static Object* Runtime_GetBreakLocations(Arguments args) {
6672 HandleScope scope;
6673 ASSERT(args.length() == 1);
6674
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 // Find the number of break points
6678 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6679 if (break_locations->IsUndefined()) return Heap::undefined_value();
6680 // Return array as JS array
6681 return *Factory::NewJSArrayWithElements(
6682 Handle<FixedArray>::cast(break_locations));
6683}
6684
6685
6686// Set a break point in a function
6687// args[0]: function
6688// args[1]: number: break source position (within the function source)
6689// args[2]: number: break point object
6690static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6691 HandleScope scope;
6692 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006693 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6694 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006695 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6696 RUNTIME_ASSERT(source_position >= 0);
6697 Handle<Object> break_point_object_arg = args.at<Object>(2);
6698
6699 // Set break point.
6700 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6701
6702 return Heap::undefined_value();
6703}
6704
6705
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006706Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6707 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006708 // Iterate the heap looking for SharedFunctionInfo generated from the
6709 // script. The inner most SharedFunctionInfo containing the source position
6710 // for the requested break point is found.
6711 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6712 // which is found is not compiled it is compiled and the heap is iterated
6713 // again as the compilation might create inner functions from the newly
6714 // compiled function and the actual requested break point might be in one of
6715 // these functions.
6716 bool done = false;
6717 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006718 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006719 Handle<SharedFunctionInfo> target;
6720 // The current candidate for the last function in script:
6721 Handle<SharedFunctionInfo> last;
6722 while (!done) {
6723 HeapIterator iterator;
6724 while (iterator.has_next()) {
6725 HeapObject* obj = iterator.next();
6726 ASSERT(obj != NULL);
6727 if (obj->IsSharedFunctionInfo()) {
6728 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6729 if (shared->script() == *script) {
6730 // If the SharedFunctionInfo found has the requested script data and
6731 // contains the source position it is a candidate.
6732 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006733 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006734 start_position = shared->start_position();
6735 }
6736 if (start_position <= position &&
6737 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006738 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006739 // candidate this is the new candidate.
6740 if (target.is_null()) {
6741 target_start_position = start_position;
6742 target = shared;
6743 } else {
6744 if (target_start_position < start_position &&
6745 shared->end_position() < target->end_position()) {
6746 target_start_position = start_position;
6747 target = shared;
6748 }
6749 }
6750 }
6751
6752 // Keep track of the last function in the script.
6753 if (last.is_null() ||
6754 shared->end_position() > last->start_position()) {
6755 last = shared;
6756 }
6757 }
6758 }
6759 }
6760
6761 // Make sure some candidate is selected.
6762 if (target.is_null()) {
6763 if (!last.is_null()) {
6764 // Position after the last function - use last.
6765 target = last;
6766 } else {
6767 // Unable to find function - possibly script without any function.
6768 return Heap::undefined_value();
6769 }
6770 }
6771
6772 // If the candidate found is compiled we are done. NOTE: when lazy
6773 // compilation of inner functions is introduced some additional checking
6774 // needs to be done here to compile inner functions.
6775 done = target->is_compiled();
6776 if (!done) {
6777 // If the candidate is not compiled compile it to reveal any inner
6778 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006779 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006780 }
6781 }
6782
6783 return *target;
6784}
6785
6786
6787// Change the state of a break point in a script. NOTE: Regarding performance
6788// see the NOTE for GetScriptFromScriptData.
6789// args[0]: script to set break point in
6790// args[1]: number: break source position (within the script source)
6791// args[2]: number: break point object
6792static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6793 HandleScope scope;
6794 ASSERT(args.length() == 3);
6795 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6796 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6797 RUNTIME_ASSERT(source_position >= 0);
6798 Handle<Object> break_point_object_arg = args.at<Object>(2);
6799
6800 // Get the script from the script wrapper.
6801 RUNTIME_ASSERT(wrapper->value()->IsScript());
6802 Handle<Script> script(Script::cast(wrapper->value()));
6803
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006804 Object* result = Runtime::FindSharedFunctionInfoInScript(
6805 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006806 if (!result->IsUndefined()) {
6807 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6808 // Find position within function. The script position might be before the
6809 // source position of the first function.
6810 int position;
6811 if (shared->start_position() > source_position) {
6812 position = 0;
6813 } else {
6814 position = source_position - shared->start_position();
6815 }
6816 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6817 }
6818 return Heap::undefined_value();
6819}
6820
6821
6822// Clear a break point
6823// args[0]: number: break point object
6824static Object* Runtime_ClearBreakPoint(Arguments args) {
6825 HandleScope scope;
6826 ASSERT(args.length() == 1);
6827 Handle<Object> break_point_object_arg = args.at<Object>(0);
6828
6829 // Clear break point.
6830 Debug::ClearBreakPoint(break_point_object_arg);
6831
6832 return Heap::undefined_value();
6833}
6834
6835
6836// Change the state of break on exceptions
6837// args[0]: boolean indicating uncaught exceptions
6838// args[1]: boolean indicating on/off
6839static Object* Runtime_ChangeBreakOnException(Arguments args) {
6840 HandleScope scope;
6841 ASSERT(args.length() == 2);
6842 ASSERT(args[0]->IsNumber());
6843 ASSERT(args[1]->IsBoolean());
6844
6845 // Update break point state
6846 ExceptionBreakType type =
6847 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6848 bool enable = args[1]->ToBoolean()->IsTrue();
6849 Debug::ChangeBreakOnException(type, enable);
6850 return Heap::undefined_value();
6851}
6852
6853
6854// Prepare for stepping
6855// args[0]: break id for checking execution state
6856// args[1]: step action from the enumeration StepAction
6857// args[2]: number of times to perform the step
6858static Object* Runtime_PrepareStep(Arguments args) {
6859 HandleScope scope;
6860 ASSERT(args.length() == 3);
6861 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006862 Object* check = Runtime_CheckExecutionState(args);
6863 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006864 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6865 return Top::Throw(Heap::illegal_argument_symbol());
6866 }
6867
6868 // Get the step action and check validity.
6869 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6870 if (step_action != StepIn &&
6871 step_action != StepNext &&
6872 step_action != StepOut &&
6873 step_action != StepInMin &&
6874 step_action != StepMin) {
6875 return Top::Throw(Heap::illegal_argument_symbol());
6876 }
6877
6878 // Get the number of steps.
6879 int step_count = NumberToInt32(args[2]);
6880 if (step_count < 1) {
6881 return Top::Throw(Heap::illegal_argument_symbol());
6882 }
6883
6884 // Prepare step.
6885 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6886 return Heap::undefined_value();
6887}
6888
6889
6890// Clear all stepping set by PrepareStep.
6891static Object* Runtime_ClearStepping(Arguments args) {
6892 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006893 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006894 Debug::ClearStepping();
6895 return Heap::undefined_value();
6896}
6897
6898
6899// Creates a copy of the with context chain. The copy of the context chain is
6900// is linked to the function context supplied.
6901static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6902 Handle<Context> function_context) {
6903 // At the bottom of the chain. Return the function context to link to.
6904 if (context_chain->is_function_context()) {
6905 return function_context;
6906 }
6907
6908 // Recursively copy the with contexts.
6909 Handle<Context> previous(context_chain->previous());
6910 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6911 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006912 CopyWithContextChain(function_context, previous),
6913 extension,
6914 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006915}
6916
6917
6918// Helper function to find or create the arguments object for
6919// Runtime_DebugEvaluate.
6920static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6921 Handle<JSFunction> function,
6922 Handle<Code> code,
6923 const ScopeInfo<>* sinfo,
6924 Handle<Context> function_context) {
6925 // Try to find the value of 'arguments' to pass as parameter. If it is not
6926 // found (that is the debugged function does not reference 'arguments' and
6927 // does not support eval) then create an 'arguments' object.
6928 int index;
6929 if (sinfo->number_of_stack_slots() > 0) {
6930 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6931 if (index != -1) {
6932 return Handle<Object>(frame->GetExpression(index));
6933 }
6934 }
6935
6936 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6937 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6938 NULL);
6939 if (index != -1) {
6940 return Handle<Object>(function_context->get(index));
6941 }
6942 }
6943
6944 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006945 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6946 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006947 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006948 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006949 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006950 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006951 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006952 return arguments;
6953}
6954
6955
6956// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006957// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006958// extension part has all the parameters and locals of the function on the
6959// stack frame. A function which calls eval with the code to evaluate is then
6960// compiled in this context and called in this context. As this context
6961// replaces the context of the function on the stack frame a new (empty)
6962// function is created as well to be used as the closure for the context.
6963// This function and the context acts as replacements for the function on the
6964// stack frame presenting the same view of the values of parameters and
6965// local variables as if the piece of JavaScript was evaluated at the point
6966// where the function on the stack frame is currently stopped.
6967static Object* Runtime_DebugEvaluate(Arguments args) {
6968 HandleScope scope;
6969
6970 // Check the execution state and decode arguments frame and source to be
6971 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006972 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006973 Object* check_result = Runtime_CheckExecutionState(args);
6974 if (check_result->IsFailure()) return check_result;
6975 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6976 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006977 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6978
6979 // Handle the processing of break.
6980 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006981
6982 // Get the frame where the debugging is performed.
6983 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6984 JavaScriptFrameIterator it(id);
6985 JavaScriptFrame* frame = it.frame();
6986 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6987 Handle<Code> code(function->code());
6988 ScopeInfo<> sinfo(*code);
6989
6990 // Traverse the saved contexts chain to find the active context for the
6991 // selected frame.
6992 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006993 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006994 save = save->prev();
6995 }
6996 ASSERT(save != NULL);
6997 SaveContext savex;
6998 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006999
7000 // Create the (empty) function replacing the function on the stack frame for
7001 // the purpose of evaluating in the context created below. It is important
7002 // that this function does not describe any parameters and local variables
7003 // in the context. If it does then this will cause problems with the lookup
7004 // in Context::Lookup, where context slots for parameters and local variables
7005 // are looked at before the extension object.
7006 Handle<JSFunction> go_between =
7007 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7008 go_between->set_context(function->context());
7009#ifdef DEBUG
7010 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7011 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7012 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7013#endif
7014
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007015 // Materialize the content of the local scope into a JSObject.
7016 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007017
7018 // Allocate a new context for the debug evaluation and set the extension
7019 // object build.
7020 Handle<Context> context =
7021 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007022 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007023 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007024 Handle<Context> frame_context(Context::cast(frame->context()));
7025 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007026 context = CopyWithContextChain(frame_context, context);
7027
7028 // Wrap the evaluation statement in a new function compiled in the newly
7029 // created context. The function has one parameter which has to be called
7030 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007031 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007032 // function(arguments,__source__) {return eval(__source__);}
7033 static const char* source_str =
7034 "function(arguments,__source__){return eval(__source__);}";
7035 static const int source_str_length = strlen(source_str);
7036 Handle<String> function_source =
7037 Factory::NewStringFromAscii(Vector<const char>(source_str,
7038 source_str_length));
7039 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007040 Compiler::CompileEval(function_source,
7041 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007042 context->IsGlobalContext(),
7043 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007044 if (boilerplate.is_null()) return Failure::Exception();
7045 Handle<JSFunction> compiled_function =
7046 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7047
7048 // Invoke the result of the compilation to get the evaluation function.
7049 bool has_pending_exception;
7050 Handle<Object> receiver(frame->receiver());
7051 Handle<Object> evaluation_function =
7052 Execution::Call(compiled_function, receiver, 0, NULL,
7053 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007054 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007055
7056 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7057 function_context);
7058
7059 // Invoke the evaluation function and return the result.
7060 const int argc = 2;
7061 Object** argv[argc] = { arguments.location(),
7062 Handle<Object>::cast(source).location() };
7063 Handle<Object> result =
7064 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7065 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007066 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007067
7068 // Skip the global proxy as it has no properties and always delegates to the
7069 // real global object.
7070 if (result->IsJSGlobalProxy()) {
7071 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7072 }
7073
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007074 return *result;
7075}
7076
7077
7078static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7079 HandleScope scope;
7080
7081 // Check the execution state and decode arguments frame and source to be
7082 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007083 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007084 Object* check_result = Runtime_CheckExecutionState(args);
7085 if (check_result->IsFailure()) return check_result;
7086 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007087 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7088
7089 // Handle the processing of break.
7090 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007091
7092 // Enter the top context from before the debugger was invoked.
7093 SaveContext save;
7094 SaveContext* top = &save;
7095 while (top != NULL && *top->context() == *Debug::debug_context()) {
7096 top = top->prev();
7097 }
7098 if (top != NULL) {
7099 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100 }
7101
7102 // Get the global context now set to the top context from before the
7103 // debugger was invoked.
7104 Handle<Context> context = Top::global_context();
7105
7106 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007107 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007108 Handle<JSFunction>(Compiler::CompileEval(source,
7109 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007110 true,
7111 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007112 if (boilerplate.is_null()) return Failure::Exception();
7113 Handle<JSFunction> compiled_function =
7114 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7115 context));
7116
7117 // Invoke the result of the compilation to get the evaluation function.
7118 bool has_pending_exception;
7119 Handle<Object> receiver = Top::global();
7120 Handle<Object> result =
7121 Execution::Call(compiled_function, receiver, 0, NULL,
7122 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007123 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007124 return *result;
7125}
7126
7127
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007128static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7129 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007130 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007131
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007132 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007133 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007134
7135 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007136 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007137 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7138 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7139 // because using
7140 // instances->set(i, *GetScriptWrapper(script))
7141 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7142 // already have deferenced the instances handle.
7143 Handle<JSValue> wrapper = GetScriptWrapper(script);
7144 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007145 }
7146
7147 // Return result as a JS array.
7148 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7149 Handle<JSArray>::cast(result)->SetContent(*instances);
7150 return *result;
7151}
7152
7153
7154// Helper function used by Runtime_DebugReferencedBy below.
7155static int DebugReferencedBy(JSObject* target,
7156 Object* instance_filter, int max_references,
7157 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007158 JSFunction* arguments_function) {
7159 NoHandleAllocation ha;
7160 AssertNoAllocation no_alloc;
7161
7162 // Iterate the heap.
7163 int count = 0;
7164 JSObject* last = NULL;
7165 HeapIterator iterator;
7166 while (iterator.has_next() &&
7167 (max_references == 0 || count < max_references)) {
7168 // Only look at all JSObjects.
7169 HeapObject* heap_obj = iterator.next();
7170 if (heap_obj->IsJSObject()) {
7171 // Skip context extension objects and argument arrays as these are
7172 // checked in the context of functions using them.
7173 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007174 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007175 obj->map()->constructor() == arguments_function) {
7176 continue;
7177 }
7178
7179 // Check if the JS object has a reference to the object looked for.
7180 if (obj->ReferencesObject(target)) {
7181 // Check instance filter if supplied. This is normally used to avoid
7182 // references from mirror objects (see Runtime_IsInPrototypeChain).
7183 if (!instance_filter->IsUndefined()) {
7184 Object* V = obj;
7185 while (true) {
7186 Object* prototype = V->GetPrototype();
7187 if (prototype->IsNull()) {
7188 break;
7189 }
7190 if (instance_filter == prototype) {
7191 obj = NULL; // Don't add this object.
7192 break;
7193 }
7194 V = prototype;
7195 }
7196 }
7197
7198 if (obj != NULL) {
7199 // Valid reference found add to instance array if supplied an update
7200 // count.
7201 if (instances != NULL && count < instances_size) {
7202 instances->set(count, obj);
7203 }
7204 last = obj;
7205 count++;
7206 }
7207 }
7208 }
7209 }
7210
7211 // Check for circular reference only. This can happen when the object is only
7212 // referenced from mirrors and has a circular reference in which case the
7213 // object is not really alive and would have been garbage collected if not
7214 // referenced from the mirror.
7215 if (count == 1 && last == target) {
7216 count = 0;
7217 }
7218
7219 // Return the number of referencing objects found.
7220 return count;
7221}
7222
7223
7224// Scan the heap for objects with direct references to an object
7225// args[0]: the object to find references to
7226// args[1]: constructor function for instances to exclude (Mirror)
7227// args[2]: the the maximum number of objects to return
7228static Object* Runtime_DebugReferencedBy(Arguments args) {
7229 ASSERT(args.length() == 3);
7230
7231 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007232 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007233
7234 // Check parameters.
7235 CONVERT_CHECKED(JSObject, target, args[0]);
7236 Object* instance_filter = args[1];
7237 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7238 instance_filter->IsJSObject());
7239 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7240 RUNTIME_ASSERT(max_references >= 0);
7241
7242 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007243 JSObject* arguments_boilerplate =
7244 Top::context()->global_context()->arguments_boilerplate();
7245 JSFunction* arguments_function =
7246 JSFunction::cast(arguments_boilerplate->map()->constructor());
7247
7248 // Get the number of referencing objects.
7249 int count;
7250 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007251 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007252
7253 // Allocate an array to hold the result.
7254 Object* object = Heap::AllocateFixedArray(count);
7255 if (object->IsFailure()) return object;
7256 FixedArray* instances = FixedArray::cast(object);
7257
7258 // Fill the referencing objects.
7259 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007260 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007261
7262 // Return result as JS array.
7263 Object* result =
7264 Heap::AllocateJSObject(
7265 Top::context()->global_context()->array_function());
7266 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7267 return result;
7268}
7269
7270
7271// Helper function used by Runtime_DebugConstructedBy below.
7272static int DebugConstructedBy(JSFunction* constructor, int max_references,
7273 FixedArray* instances, int instances_size) {
7274 AssertNoAllocation no_alloc;
7275
7276 // Iterate the heap.
7277 int count = 0;
7278 HeapIterator iterator;
7279 while (iterator.has_next() &&
7280 (max_references == 0 || count < max_references)) {
7281 // Only look at all JSObjects.
7282 HeapObject* heap_obj = iterator.next();
7283 if (heap_obj->IsJSObject()) {
7284 JSObject* obj = JSObject::cast(heap_obj);
7285 if (obj->map()->constructor() == constructor) {
7286 // Valid reference found add to instance array if supplied an update
7287 // count.
7288 if (instances != NULL && count < instances_size) {
7289 instances->set(count, obj);
7290 }
7291 count++;
7292 }
7293 }
7294 }
7295
7296 // Return the number of referencing objects found.
7297 return count;
7298}
7299
7300
7301// Scan the heap for objects constructed by a specific function.
7302// args[0]: the constructor to find instances of
7303// args[1]: the the maximum number of objects to return
7304static Object* Runtime_DebugConstructedBy(Arguments args) {
7305 ASSERT(args.length() == 2);
7306
7307 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007308 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007309
7310 // Check parameters.
7311 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7312 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7313 RUNTIME_ASSERT(max_references >= 0);
7314
7315 // Get the number of referencing objects.
7316 int count;
7317 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7318
7319 // Allocate an array to hold the result.
7320 Object* object = Heap::AllocateFixedArray(count);
7321 if (object->IsFailure()) return object;
7322 FixedArray* instances = FixedArray::cast(object);
7323
7324 // Fill the referencing objects.
7325 count = DebugConstructedBy(constructor, max_references, instances, count);
7326
7327 // Return result as JS array.
7328 Object* result =
7329 Heap::AllocateJSObject(
7330 Top::context()->global_context()->array_function());
7331 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7332 return result;
7333}
7334
7335
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007336// Find the effective prototype object as returned by __proto__.
7337// args[0]: the object to find the prototype for.
7338static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007339 ASSERT(args.length() == 1);
7340
7341 CONVERT_CHECKED(JSObject, obj, args[0]);
7342
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007343 // Use the __proto__ accessor.
7344 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007345}
7346
7347
7348static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007349 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007350 CPU::DebugBreak();
7351 return Heap::undefined_value();
7352}
7353
7354
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007355static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
7356#ifdef DEBUG
7357 HandleScope scope;
7358 ASSERT(args.length() == 1);
7359 // Get the function and make sure it is compiled.
7360 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7361 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7362 return Failure::Exception();
7363 }
7364 func->code()->PrintLn();
7365#endif // DEBUG
7366 return Heap::undefined_value();
7367}
ager@chromium.org9085a012009-05-11 19:22:57 +00007368
7369
7370static Object* Runtime_FunctionGetInferredName(Arguments args) {
7371 NoHandleAllocation ha;
7372 ASSERT(args.length() == 1);
7373
7374 CONVERT_CHECKED(JSFunction, f, args[0]);
7375 return f->shared()->inferred_name();
7376}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007377#endif // ENABLE_DEBUGGER_SUPPORT
7378
7379
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007380// Finds the script object from the script data. NOTE: This operation uses
7381// heap traversal to find the function generated for the source position
7382// for the requested break point. For lazily compiled functions several heap
7383// traversals might be required rendering this operation as a rather slow
7384// operation. However for setting break points which is normally done through
7385// some kind of user interaction the performance is not crucial.
7386static Handle<Object> Runtime_GetScriptFromScriptName(
7387 Handle<String> script_name) {
7388 // Scan the heap for Script objects to find the script with the requested
7389 // script data.
7390 Handle<Script> script;
7391 HeapIterator iterator;
7392 while (script.is_null() && iterator.has_next()) {
7393 HeapObject* obj = iterator.next();
7394 // If a script is found check if it has the script data requested.
7395 if (obj->IsScript()) {
7396 if (Script::cast(obj)->name()->IsString()) {
7397 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7398 script = Handle<Script>(Script::cast(obj));
7399 }
7400 }
7401 }
7402 }
7403
7404 // If no script with the requested script data is found return undefined.
7405 if (script.is_null()) return Factory::undefined_value();
7406
7407 // Return the script found.
7408 return GetScriptWrapper(script);
7409}
7410
7411
7412// Get the script object from script data. NOTE: Regarding performance
7413// see the NOTE for GetScriptFromScriptData.
7414// args[0]: script data for the script to find the source for
7415static Object* Runtime_GetScript(Arguments args) {
7416 HandleScope scope;
7417
7418 ASSERT(args.length() == 1);
7419
7420 CONVERT_CHECKED(String, script_name, args[0]);
7421
7422 // Find the requested script.
7423 Handle<Object> result =
7424 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7425 return *result;
7426}
7427
7428
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007429static Object* Runtime_Abort(Arguments args) {
7430 ASSERT(args.length() == 2);
7431 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7432 Smi::cast(args[1])->value());
7433 Top::PrintStack();
7434 OS::Abort();
7435 UNREACHABLE();
7436 return NULL;
7437}
7438
7439
kasper.lund44510672008-07-25 07:37:58 +00007440#ifdef DEBUG
7441// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7442// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007443static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007444 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007445 HandleScope scope;
7446 Handle<JSArray> result = Factory::NewJSArray(0);
7447 int index = 0;
7448#define ADD_ENTRY(Name, argc) \
7449 { \
7450 HandleScope inner; \
7451 Handle<String> name = \
7452 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7453 Handle<JSArray> pair = Factory::NewJSArray(0); \
7454 SetElement(pair, 0, name); \
7455 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7456 SetElement(result, index++, pair); \
7457 }
7458 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7459#undef ADD_ENTRY
7460 return *result;
7461}
kasper.lund44510672008-07-25 07:37:58 +00007462#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007463
7464
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007465static Object* Runtime_Log(Arguments args) {
7466 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007467 CONVERT_CHECKED(String, format, args[0]);
7468 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007469 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007470 Logger::LogRuntime(chars, elms);
7471 return Heap::undefined_value();
7472}
7473
7474
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007475static Object* Runtime_IS_VAR(Arguments args) {
7476 UNREACHABLE(); // implemented as macro in the parser
7477 return NULL;
7478}
7479
7480
7481// ----------------------------------------------------------------------------
7482// Implementation of Runtime
7483
7484#define F(name, nargs) \
7485 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7486 static_cast<int>(Runtime::k##name) },
7487
7488static Runtime::Function Runtime_functions[] = {
7489 RUNTIME_FUNCTION_LIST(F)
7490 { NULL, NULL, NULL, 0, -1 }
7491};
7492
7493#undef F
7494
7495
7496Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7497 ASSERT(0 <= fid && fid < kNofFunctions);
7498 return &Runtime_functions[fid];
7499}
7500
7501
7502Runtime::Function* Runtime::FunctionForName(const char* name) {
7503 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7504 if (strcmp(f->name, name) == 0) {
7505 return f;
7506 }
7507 }
7508 return NULL;
7509}
7510
7511
7512void Runtime::PerformGC(Object* result) {
7513 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007514 if (failure->IsRetryAfterGC()) {
7515 // Try to do a garbage collection; ignore it if it fails. The C
7516 // entry stub will throw an out-of-memory exception in that case.
7517 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7518 } else {
7519 // Handle last resort GC and make sure to allow future allocations
7520 // to grow the heap without causing GCs (if possible).
7521 Counters::gc_last_resort_from_js.Increment();
7522 Heap::CollectAllGarbage();
7523 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007524}
7525
7526
7527} } // namespace v8::internal