blob: f5b366fbe056df2b7f8a2563dafea70b5a0fd0a9 [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
49namespace v8 { namespace internal {
50
51
52#define RUNTIME_ASSERT(value) do { \
53 if (!(value)) return IllegalOperation(); \
54} while (false)
55
56// Cast the given object to a value of the specified type and store
57// it in a variable with the given name. If the object is not of the
58// expected type call IllegalOperation and return.
59#define CONVERT_CHECKED(Type, name, obj) \
60 RUNTIME_ASSERT(obj->Is##Type()); \
61 Type* name = Type::cast(obj);
62
63#define CONVERT_ARG_CHECKED(Type, name, index) \
64 RUNTIME_ASSERT(args[index]->Is##Type()); \
65 Handle<Type> name = args.at<Type>(index);
66
kasper.lundbd3ec4e2008-07-09 11:06:54 +000067// Cast the given object to a boolean and store it in a variable with
68// the given name. If the object is not a boolean call IllegalOperation
69// and return.
70#define CONVERT_BOOLEAN_CHECKED(name, obj) \
71 RUNTIME_ASSERT(obj->IsBoolean()); \
72 bool name = (obj)->IsTrue();
73
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000074// Cast the given object to a Smi and store its value in an int variable
75// with the given name. If the object is not a Smi call IllegalOperation
76// and return.
77#define CONVERT_SMI_CHECKED(name, obj) \
78 RUNTIME_ASSERT(obj->IsSmi()); \
79 int name = Smi::cast(obj)->value();
80
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000081// Cast the given object to a double and store it in a variable with
82// the given name. If the object is not a number (as opposed to
83// the number not-a-number) call IllegalOperation and return.
84#define CONVERT_DOUBLE_CHECKED(name, obj) \
85 RUNTIME_ASSERT(obj->IsNumber()); \
86 double name = (obj)->Number();
87
88// Call the specified converter on the object *comand store the result in
89// a variable of the specified type with the given name. If the
90// object is not a Number call IllegalOperation and return.
91#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
92 RUNTIME_ASSERT(obj->IsNumber()); \
93 type name = NumberTo##Type(obj);
94
95// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000096static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097
98
99static Object* IllegalOperation() {
100 return Top::Throw(Heap::illegal_access_symbol());
101}
102
103
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000104static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
105 StackLimitCheck check;
106 if (check.HasOverflowed()) return Top::StackOverflow();
107
108 Object* result = Heap::CopyJSObject(boilerplate);
109 if (result->IsFailure()) return result;
110 JSObject* copy = JSObject::cast(result);
111
112 // Deep copy local properties.
113 if (copy->HasFastProperties()) {
114 FixedArray* properties = copy->properties();
115 WriteBarrierMode mode = properties->GetWriteBarrierMode();
116 for (int i = 0; i < properties->length(); i++) {
117 Object* value = properties->get(i);
118 if (value->IsJSObject()) {
119 JSObject* jsObject = JSObject::cast(value);
120 result = DeepCopyBoilerplate(jsObject);
121 if (result->IsFailure()) return result;
122 properties->set(i, result, mode);
123 }
124 }
125 mode = copy->GetWriteBarrierMode();
126 for (int i = 0; i < copy->map()->inobject_properties(); i++) {
127 Object* value = copy->InObjectPropertyAt(i);
128 if (value->IsJSObject()) {
129 JSObject* jsObject = JSObject::cast(value);
130 result = DeepCopyBoilerplate(jsObject);
131 if (result->IsFailure()) return result;
132 copy->InObjectPropertyAtPut(i, result, mode);
133 }
134 }
135 } else {
136 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
137 if (result->IsFailure()) return result;
138 FixedArray* names = FixedArray::cast(result);
139 copy->GetLocalPropertyNames(names, 0);
140 for (int i = 0; i < names->length(); i++) {
141 ASSERT(names->get(i)->IsString());
142 String* keyString = String::cast(names->get(i));
143 PropertyAttributes attributes =
144 copy->GetLocalPropertyAttribute(keyString);
145 // Only deep copy fields from the object literal expression.
146 // In particular, don't try to copy the length attribute of
147 // an array.
148 if (attributes != NONE) continue;
149 Object* value = copy->GetProperty(keyString, &attributes);
150 ASSERT(!value->IsFailure());
151 if (value->IsJSObject()) {
152 JSObject* jsObject = JSObject::cast(value);
153 result = DeepCopyBoilerplate(jsObject);
154 if (result->IsFailure()) return result;
155 result = copy->SetProperty(keyString, result, NONE);
156 if (result->IsFailure()) return result;
157 }
158 }
159 }
160
161 // Deep copy local elements.
162 if (copy->HasFastElements()) {
163 FixedArray* elements = copy->elements();
164 WriteBarrierMode mode = elements->GetWriteBarrierMode();
165 for (int i = 0; i < elements->length(); i++) {
166 Object* value = elements->get(i);
167 if (value->IsJSObject()) {
168 JSObject* jsObject = JSObject::cast(value);
169 result = DeepCopyBoilerplate(jsObject);
170 if (result->IsFailure()) return result;
171 elements->set(i, result, mode);
172 }
173 }
174 } else {
175 Dictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
182 JSObject* jsObject = JSObject::cast(value);
183 result = DeepCopyBoilerplate(jsObject);
184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 }
190 return copy;
191}
192
193
194static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
195 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
196 return DeepCopyBoilerplate(boilerplate);
197}
198
199
200static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000201 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000202 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000203}
204
205
ager@chromium.org236ad962008-09-25 09:45:57 +0000206static Handle<Map> ComputeObjectLiteralMap(
207 Handle<Context> context,
208 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000209 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000210 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000211 if (FLAG_canonicalize_object_literal_maps) {
212 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000213 int number_of_symbol_keys = 0;
214 while ((number_of_symbol_keys < number_of_properties) &&
215 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
216 number_of_symbol_keys++;
217 }
218 // Based on the number of prefix symbols key we decide whether
219 // to use the map cache in the global context.
220 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000221 if ((number_of_symbol_keys == number_of_properties) &&
222 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000223 // Create the fixed array with the key.
224 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
225 for (int i = 0; i < number_of_symbol_keys; i++) {
226 keys->set(i, constant_properties->get(i*2));
227 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000228 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000229 return Factory::ObjectLiteralMapFromCache(context, keys);
230 }
231 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000232 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000233 return Factory::CopyMap(
234 Handle<Map>(context->object_function()->initial_map()),
235 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000236}
237
238
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000239static Handle<Object> CreateLiteralBoilerplate(
240 Handle<FixedArray> literals,
241 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000242
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000243
244static Handle<Object> CreateObjectLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247 // Get the global context from the literals array. This is the
248 // context in which the function was created and we use the object
249 // function from this context to create the object literal. We do
250 // not use the object function from the current global context
251 // because this might be the object function from another context
252 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000253 Handle<Context> context =
254 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
255
256 bool is_result_from_cache;
257 Handle<Map> map = ComputeObjectLiteralMap(context,
258 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000259 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000260
ager@chromium.org236ad962008-09-25 09:45:57 +0000261 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000262 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000263 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000264 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
265 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266 for (int index = 0; index < length; index +=2) {
267 Handle<Object> key(constant_properties->get(index+0));
268 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000269 if (value->IsFixedArray()) {
270 // The value contains the constant_properties of a
271 // simple object literal.
272 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
273 value = CreateLiteralBoilerplate(literals, array);
274 if (value.is_null()) return value;
275 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000276 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 uint32_t element_index = 0;
278 if (key->IsSymbol()) {
279 // If key is a symbol it is not an array element.
280 Handle<String> name(String::cast(*key));
281 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000282 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000283 } else if (Array::IndexFromObject(*key, &element_index)) {
284 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000285 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000286 } else {
287 // Non-uint32 number.
288 ASSERT(key->IsNumber());
289 double num = key->Number();
290 char arr[100];
291 Vector<char> buffer(arr, ARRAY_SIZE(arr));
292 const char* str = DoubleToCString(num, buffer);
293 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000294 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 // If setting the property on the boilerplate throws an
297 // exception, the exception is converted to an empty handle in
298 // the handle based operations. In that case, we need to
299 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000300 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 }
302 }
303
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000304 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000305}
306
307
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000308static Handle<Object> CreateArrayLiteralBoilerplate(
309 Handle<FixedArray> literals,
310 Handle<FixedArray> elements) {
311 // Create the JSArray.
312 Handle<JSFunction> constructor(
313 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
314 Handle<Object> object = Factory::NewJSObject(constructor);
315
316 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
317
318 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
319 for (int i = 0; i < content->length(); i++) {
320 if (content->get(i)->IsFixedArray()) {
321 // The value contains the constant_properties of a
322 // simple object literal.
323 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
324 Handle<Object> result =
325 CreateLiteralBoilerplate(literals, fa);
326 if (result.is_null()) return result;
327 content->set(i, *result);
328 }
329 }
330
331 // Set the elements.
332 Handle<JSArray>::cast(object)->SetContent(*content);
333 return object;
334}
335
336
337static Handle<Object> CreateLiteralBoilerplate(
338 Handle<FixedArray> literals,
339 Handle<FixedArray> array) {
340 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
341 switch (CompileTimeValue::GetType(array)) {
342 case CompileTimeValue::OBJECT_LITERAL:
343 return CreateObjectLiteralBoilerplate(literals, elements);
344 case CompileTimeValue::ARRAY_LITERAL:
345 return CreateArrayLiteralBoilerplate(literals, elements);
346 default:
347 UNREACHABLE();
348 return Handle<Object>::null();
349 }
350}
351
352
353static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
354 HandleScope scope;
355 ASSERT(args.length() == 3);
356 // Copy the arguments.
357 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
358 CONVERT_SMI_CHECKED(literals_index, args[1]);
359 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
360
361 Handle<Object> result =
362 CreateObjectLiteralBoilerplate(literals, constant_properties);
363
364 if (result.is_null()) return Failure::Exception();
365
366 // Update the functions literal and return the boilerplate.
367 literals->set(literals_index, *result);
368
369 return *result;
370}
371
372
373static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000374 // Takes a FixedArray of elements containing the literal elements of
375 // the array literal and produces JSArray with those elements.
376 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000377 // which contains the context from which to get the Array function
378 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000379 HandleScope scope;
380 ASSERT(args.length() == 3);
381 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
382 CONVERT_SMI_CHECKED(literals_index, args[1]);
383 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000385 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
386 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000387
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000388 // Update the functions literal and return the boilerplate.
389 literals->set(literals_index, *object);
390 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391}
392
393
ager@chromium.org32912102009-01-16 10:38:43 +0000394static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
395 ASSERT(args.length() == 2);
396 CONVERT_CHECKED(String, key, args[0]);
397 Object* value = args[1];
398 // Create a catch context extension object.
399 JSFunction* constructor =
400 Top::context()->global_context()->context_extension_function();
401 Object* object = Heap::AllocateJSObject(constructor);
402 if (object->IsFailure()) return object;
403 // Assign the exception value to the catch variable and make sure
404 // that the catch variable is DontDelete.
405 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
406 if (value->IsFailure()) return value;
407 return object;
408}
409
410
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000411static Object* Runtime_ClassOf(Arguments args) {
412 NoHandleAllocation ha;
413 ASSERT(args.length() == 1);
414 Object* obj = args[0];
415 if (!obj->IsJSObject()) return Heap::null_value();
416 return JSObject::cast(obj)->class_name();
417}
418
ager@chromium.org7c537e22008-10-16 08:43:32 +0000419
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000420static Object* Runtime_HasStringClass(Arguments args) {
421 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000422}
423
424
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000425static Object* Runtime_HasDateClass(Arguments args) {
426 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000427}
428
429
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000430static Object* Runtime_HasArrayClass(Arguments args) {
431 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
432}
433
434
435static Object* Runtime_HasFunctionClass(Arguments args) {
436 return Heap::ToBoolean(
437 args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
438}
439
440
441static Object* Runtime_HasNumberClass(Arguments args) {
442 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
443}
444
445
446static Object* Runtime_HasBooleanClass(Arguments args) {
447 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
448}
449
450
451static Object* Runtime_HasArgumentsClass(Arguments args) {
452 return Heap::ToBoolean(
453 args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
454}
455
456
457static Object* Runtime_HasRegExpClass(Arguments args) {
458 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000459}
460
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000461
462static Object* Runtime_IsInPrototypeChain(Arguments args) {
463 NoHandleAllocation ha;
464 ASSERT(args.length() == 2);
465 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
466 Object* O = args[0];
467 Object* V = args[1];
468 while (true) {
469 Object* prototype = V->GetPrototype();
470 if (prototype->IsNull()) return Heap::false_value();
471 if (O == prototype) return Heap::true_value();
472 V = prototype;
473 }
474}
475
476
ager@chromium.org9085a012009-05-11 19:22:57 +0000477// Inserts an object as the hidden prototype of another object.
478static Object* Runtime_SetHiddenPrototype(Arguments args) {
479 NoHandleAllocation ha;
480 ASSERT(args.length() == 2);
481 CONVERT_CHECKED(JSObject, jsobject, args[0]);
482 CONVERT_CHECKED(JSObject, proto, args[1]);
483
484 // Sanity checks. The old prototype (that we are replacing) could
485 // theoretically be null, but if it is not null then check that we
486 // didn't already install a hidden prototype here.
487 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
488 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
489 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
490
491 // Allocate up front before we start altering state in case we get a GC.
492 Object* map_or_failure = proto->map()->CopyDropTransitions();
493 if (map_or_failure->IsFailure()) return map_or_failure;
494 Map* new_proto_map = Map::cast(map_or_failure);
495
496 map_or_failure = jsobject->map()->CopyDropTransitions();
497 if (map_or_failure->IsFailure()) return map_or_failure;
498 Map* new_map = Map::cast(map_or_failure);
499
500 // Set proto's prototype to be the old prototype of the object.
501 new_proto_map->set_prototype(jsobject->GetPrototype());
502 proto->set_map(new_proto_map);
503 new_proto_map->set_is_hidden_prototype();
504
505 // Set the object's prototype to proto.
506 new_map->set_prototype(proto);
507 jsobject->set_map(new_map);
508
509 return Heap::undefined_value();
510}
511
512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000513static Object* Runtime_IsConstructCall(Arguments args) {
514 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000515 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516 JavaScriptFrameIterator it;
517 return Heap::ToBoolean(it.frame()->IsConstructor());
518}
519
520
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000521static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000522 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000523 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +0000524 CONVERT_CHECKED(JSRegExp, raw_re, args[0]);
525 Handle<JSRegExp> re(raw_re);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000526 CONVERT_CHECKED(String, raw_pattern, args[1]);
527 Handle<String> pattern(raw_pattern);
528 CONVERT_CHECKED(String, raw_flags, args[2]);
529 Handle<String> flags(raw_flags);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000530 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
531 if (result.is_null()) return Failure::Exception();
532 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000533}
534
535
536static Object* Runtime_CreateApiFunction(Arguments args) {
537 HandleScope scope;
538 ASSERT(args.length() == 1);
539 CONVERT_CHECKED(FunctionTemplateInfo, raw_data, args[0]);
540 Handle<FunctionTemplateInfo> data(raw_data);
541 return *Factory::CreateApiFunction(data);
542}
543
544
545static Object* Runtime_IsTemplate(Arguments args) {
546 ASSERT(args.length() == 1);
547 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000548 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000549 return Heap::ToBoolean(result);
550}
551
552
553static Object* Runtime_GetTemplateField(Arguments args) {
554 ASSERT(args.length() == 2);
555 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000556 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000557 int index = field->value();
558 int offset = index * kPointerSize + HeapObject::kHeaderSize;
559 InstanceType type = templ->map()->instance_type();
560 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
561 type == OBJECT_TEMPLATE_INFO_TYPE);
562 RUNTIME_ASSERT(offset > 0);
563 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
564 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
565 } else {
566 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
567 }
568 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000569}
570
571
ager@chromium.org870a0b62008-11-04 11:43:05 +0000572static Object* Runtime_DisableAccessChecks(Arguments args) {
573 ASSERT(args.length() == 1);
574 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000575 Map* old_map = object->map();
576 bool needs_access_checks = old_map->is_access_check_needed();
577 if (needs_access_checks) {
578 // Copy map so it won't interfere constructor's initial map.
579 Object* new_map = old_map->CopyDropTransitions();
580 if (new_map->IsFailure()) return new_map;
581
582 Map::cast(new_map)->set_is_access_check_needed(false);
583 object->set_map(Map::cast(new_map));
584 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000585 return needs_access_checks ? Heap::true_value() : Heap::false_value();
586}
587
588
589static Object* Runtime_EnableAccessChecks(Arguments args) {
590 ASSERT(args.length() == 1);
591 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000592 Map* old_map = object->map();
593 if (!old_map->is_access_check_needed()) {
594 // Copy map so it won't interfere constructor's initial map.
595 Object* new_map = old_map->CopyDropTransitions();
596 if (new_map->IsFailure()) return new_map;
597
598 Map::cast(new_map)->set_is_access_check_needed(true);
599 object->set_map(Map::cast(new_map));
600 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000601 return Heap::undefined_value();
602}
603
604
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000605static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
606 HandleScope scope;
607 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
608 Handle<Object> args[2] = { type_handle, name };
609 Handle<Object> error =
610 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
611 return Top::Throw(*error);
612}
613
614
615static Object* Runtime_DeclareGlobals(Arguments args) {
616 HandleScope scope;
617 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
618
619 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
620 Handle<Context> context = args.at<Context>(1);
621 bool is_eval = Smi::cast(args[2])->value() == 1;
622
623 // Compute the property attributes. According to ECMA-262, section
624 // 13, page 71, the property must be read-only and
625 // non-deletable. However, neither SpiderMonkey nor KJS creates the
626 // property as read-only, so we don't either.
627 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
628
629 // Only optimize the object if we intend to add more than 5 properties.
630 OptimizedObjectForAddingMultipleProperties ba(global, pairs->length()/2 > 5);
631
632 // Traverse the name/value pairs and set the properties.
633 int length = pairs->length();
634 for (int i = 0; i < length; i += 2) {
635 HandleScope scope;
636 Handle<String> name(String::cast(pairs->get(i)));
637 Handle<Object> value(pairs->get(i + 1));
638
639 // We have to declare a global const property. To capture we only
640 // assign to it when evaluating the assignment for "const x =
641 // <expr>" the initial value is the hole.
642 bool is_const_property = value->IsTheHole();
643
644 if (value->IsUndefined() || is_const_property) {
645 // Lookup the property in the global object, and don't set the
646 // value of the variable if the property is already there.
647 LookupResult lookup;
648 global->Lookup(*name, &lookup);
649 if (lookup.IsProperty()) {
650 // Determine if the property is local by comparing the holder
651 // against the global object. The information will be used to
652 // avoid throwing re-declaration errors when declaring
653 // variables or constants that exist in the prototype chain.
654 bool is_local = (*global == lookup.holder());
655 // Get the property attributes and determine if the property is
656 // read-only.
657 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
658 bool is_read_only = (attributes & READ_ONLY) != 0;
659 if (lookup.type() == INTERCEPTOR) {
660 // If the interceptor says the property is there, we
661 // just return undefined without overwriting the property.
662 // Otherwise, we continue to setting the property.
663 if (attributes != ABSENT) {
664 // Check if the existing property conflicts with regards to const.
665 if (is_local && (is_read_only || is_const_property)) {
666 const char* type = (is_read_only) ? "const" : "var";
667 return ThrowRedeclarationError(type, name);
668 };
669 // The property already exists without conflicting: Go to
670 // the next declaration.
671 continue;
672 }
673 // Fall-through and introduce the absent property by using
674 // SetProperty.
675 } else {
676 if (is_local && (is_read_only || is_const_property)) {
677 const char* type = (is_read_only) ? "const" : "var";
678 return ThrowRedeclarationError(type, name);
679 }
680 // The property already exists without conflicting: Go to
681 // the next declaration.
682 continue;
683 }
684 }
685 } else {
686 // Copy the function and update its context. Use it as value.
687 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
688 Handle<JSFunction> function =
689 Factory::NewFunctionFromBoilerplate(boilerplate, context);
690 value = function;
691 }
692
693 LookupResult lookup;
694 global->LocalLookup(*name, &lookup);
695
696 PropertyAttributes attributes = is_const_property
697 ? static_cast<PropertyAttributes>(base | READ_ONLY)
698 : base;
699
700 if (lookup.IsProperty()) {
701 // There's a local property that we need to overwrite because
702 // we're either declaring a function or there's an interceptor
703 // that claims the property is absent.
704
705 // Check for conflicting re-declarations. We cannot have
706 // conflicting types in case of intercepted properties because
707 // they are absent.
708 if (lookup.type() != INTERCEPTOR &&
709 (lookup.IsReadOnly() || is_const_property)) {
710 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
711 return ThrowRedeclarationError(type, name);
712 }
713 SetProperty(global, name, value, attributes);
714 } else {
715 // If a property with this name does not already exist on the
716 // global object add the property locally. We take special
717 // precautions to always add it as a local property even in case
718 // of callbacks in the prototype chain (this rules out using
719 // SetProperty). Also, we must use the handle-based version to
720 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000721 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000722 }
723 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000724
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725 return Heap::undefined_value();
726}
727
728
729static Object* Runtime_DeclareContextSlot(Arguments args) {
730 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000731 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000732
ager@chromium.org7c537e22008-10-16 08:43:32 +0000733 CONVERT_ARG_CHECKED(Context, context, 0);
734 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000735 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000736 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000738 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739
740 // Declarations are always done in the function context.
741 context = Handle<Context>(context->fcontext());
742
743 int index;
744 PropertyAttributes attributes;
745 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000746 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000747 context->Lookup(name, flags, &index, &attributes);
748
749 if (attributes != ABSENT) {
750 // The name was declared before; check for conflicting
751 // re-declarations: This is similar to the code in parser.cc in
752 // the AstBuildingParser::Declare function.
753 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
754 // Functions are not read-only.
755 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
756 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
757 return ThrowRedeclarationError(type, name);
758 }
759
760 // Initialize it if necessary.
761 if (*initial_value != NULL) {
762 if (index >= 0) {
763 // The variable or constant context slot should always be in
764 // the function context; not in any outer context nor in the
765 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000766 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000767 if (((attributes & READ_ONLY) == 0) ||
768 context->get(index)->IsTheHole()) {
769 context->set(index, *initial_value);
770 }
771 } else {
772 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000773 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000774 SetProperty(context_ext, name, initial_value, mode);
775 }
776 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000777
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000778 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000779 // The property is not in the function context. It needs to be
780 // "declared" in the function context's extension context, or in the
781 // global context.
782 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000783 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000784 // The function context's extension context exists - use it.
785 context_ext = Handle<JSObject>(context->extension());
786 } else {
787 // The function context's extension context does not exists - allocate
788 // it.
789 context_ext = Factory::NewJSObject(Top::context_extension_function());
790 // And store it in the extension slot.
791 context->set_extension(*context_ext);
792 }
793 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000794
ager@chromium.org7c537e22008-10-16 08:43:32 +0000795 // Declare the property by setting it to the initial value if provided,
796 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
797 // constant declarations).
798 ASSERT(!context_ext->HasLocalProperty(*name));
799 Handle<Object> value(Heap::undefined_value());
800 if (*initial_value != NULL) value = initial_value;
801 SetProperty(context_ext, name, value, mode);
802 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
803 }
804
805 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000806}
807
808
809static Object* Runtime_InitializeVarGlobal(Arguments args) {
810 NoHandleAllocation nha;
811
812 // Determine if we need to assign to the variable if it already
813 // exists (based on the number of arguments).
814 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
815 bool assign = args.length() == 2;
816
817 CONVERT_ARG_CHECKED(String, name, 0);
818 GlobalObject* global = Top::context()->global();
819
820 // According to ECMA-262, section 12.2, page 62, the property must
821 // not be deletable.
822 PropertyAttributes attributes = DONT_DELETE;
823
824 // Lookup the property locally in the global object. If it isn't
825 // there, we add the property and take special precautions to always
826 // add it as a local property even in case of callbacks in the
827 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000828 // We have IgnoreAttributesAndSetLocalProperty for this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000829 LookupResult lookup;
830 global->LocalLookup(*name, &lookup);
831 if (!lookup.IsProperty()) {
832 Object* value = (assign) ? args[1] : Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000833 return global->IgnoreAttributesAndSetLocalProperty(*name,
834 value,
835 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000836 }
837
838 // Determine if this is a redeclaration of something read-only.
839 if (lookup.IsReadOnly()) {
840 return ThrowRedeclarationError("const", name);
841 }
842
843 // Determine if this is a redeclaration of an intercepted read-only
844 // property and figure out if the property exists at all.
845 bool found = true;
846 PropertyType type = lookup.type();
847 if (type == INTERCEPTOR) {
848 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
849 if (intercepted == ABSENT) {
850 // The interceptor claims the property isn't there. We need to
851 // make sure to introduce it.
852 found = false;
853 } else if ((intercepted & READ_ONLY) != 0) {
854 // The property is present, but read-only. Since we're trying to
855 // overwrite it with a variable declaration we must throw a
856 // re-declaration error.
857 return ThrowRedeclarationError("const", name);
858 }
859 // Restore global object from context (in case of GC).
860 global = Top::context()->global();
861 }
862
863 if (found && !assign) {
864 // The global property is there and we're not assigning any value
865 // to it. Just return.
866 return Heap::undefined_value();
867 }
868
869 // Assign the value (or undefined) to the property.
870 Object* value = (assign) ? args[1] : Heap::undefined_value();
871 return global->SetProperty(&lookup, *name, value, attributes);
872}
873
874
875static Object* Runtime_InitializeConstGlobal(Arguments args) {
876 // All constants are declared with an initial value. The name
877 // of the constant is the first argument and the initial value
878 // is the second.
879 RUNTIME_ASSERT(args.length() == 2);
880 CONVERT_ARG_CHECKED(String, name, 0);
881 Handle<Object> value = args.at<Object>(1);
882
883 // Get the current global object from top.
884 GlobalObject* global = Top::context()->global();
885
886 // According to ECMA-262, section 12.2, page 62, the property must
887 // not be deletable. Since it's a const, it must be READ_ONLY too.
888 PropertyAttributes attributes =
889 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
890
891 // Lookup the property locally in the global object. If it isn't
892 // there, we add the property and take special precautions to always
893 // add it as a local property even in case of callbacks in the
894 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000895 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000896 LookupResult lookup;
897 global->LocalLookup(*name, &lookup);
898 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000899 return global->IgnoreAttributesAndSetLocalProperty(*name,
900 *value,
901 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902 }
903
904 // Determine if this is a redeclaration of something not
905 // read-only. In case the result is hidden behind an interceptor we
906 // need to ask it for the property attributes.
907 if (!lookup.IsReadOnly()) {
908 if (lookup.type() != INTERCEPTOR) {
909 return ThrowRedeclarationError("var", name);
910 }
911
912 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
913
914 // Throw re-declaration error if the intercepted property is present
915 // but not read-only.
916 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
917 return ThrowRedeclarationError("var", name);
918 }
919
920 // Restore global object from context (in case of GC) and continue
921 // with setting the value because the property is either absent or
922 // read-only. We also have to do redo the lookup.
923 global = Top::context()->global();
924
925 // BUG 1213579: Handle the case where we have to set a read-only
926 // property through an interceptor and only do it if it's
927 // uninitialized, e.g. the hole. Nirk...
928 global->SetProperty(*name, *value, attributes);
929 return *value;
930 }
931
932 // Set the value, but only we're assigning the initial value to a
933 // constant. For now, we determine this by checking if the
934 // current value is the hole.
935 PropertyType type = lookup.type();
936 if (type == FIELD) {
937 FixedArray* properties = global->properties();
938 int index = lookup.GetFieldIndex();
939 if (properties->get(index)->IsTheHole()) {
940 properties->set(index, *value);
941 }
942 } else if (type == NORMAL) {
943 Dictionary* dictionary = global->property_dictionary();
944 int entry = lookup.GetDictionaryEntry();
945 if (dictionary->ValueAt(entry)->IsTheHole()) {
946 dictionary->ValueAtPut(entry, *value);
947 }
948 } else {
949 // Ignore re-initialization of constants that have already been
950 // assigned a function value.
951 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
952 }
953
954 // Use the set value as the result of the operation.
955 return *value;
956}
957
958
959static Object* Runtime_InitializeConstContextSlot(Arguments args) {
960 HandleScope scope;
961 ASSERT(args.length() == 3);
962
963 Handle<Object> value(args[0]);
964 ASSERT(!value->IsTheHole());
965 CONVERT_ARG_CHECKED(Context, context, 1);
966 Handle<String> name(String::cast(args[2]));
967
968 // Initializations are always done in the function context.
969 context = Handle<Context>(context->fcontext());
970
971 int index;
972 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000973 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000974 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000975 context->Lookup(name, flags, &index, &attributes);
976
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000977 // In most situations, the property introduced by the const
978 // declaration should be present in the context extension object.
979 // However, because declaration and initialization are separate, the
980 // property might have been deleted (if it was introduced by eval)
981 // before we reach the initialization point.
982 //
983 // Example:
984 //
985 // function f() { eval("delete x; const x;"); }
986 //
987 // In that case, the initialization behaves like a normal assignment
988 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000989 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000990 // Property was found in a context.
991 if (holder->IsContext()) {
992 // The holder cannot be the function context. If it is, there
993 // should have been a const redeclaration error when declaring
994 // the const property.
995 ASSERT(!holder.is_identical_to(context));
996 if ((attributes & READ_ONLY) == 0) {
997 Handle<Context>::cast(holder)->set(index, *value);
998 }
999 } else {
1000 // The holder is an arguments object.
1001 ASSERT((attributes & READ_ONLY) == 0);
1002 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001003 }
1004 return *value;
1005 }
1006
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001007 // The property could not be found, we introduce it in the global
1008 // context.
1009 if (attributes == ABSENT) {
1010 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1011 SetProperty(global, name, value, NONE);
1012 return *value;
1013 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001014
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001015 // The property was present in a context extension object.
1016 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001017
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001018 if (*context_ext == context->extension()) {
1019 // This is the property that was introduced by the const
1020 // declaration. Set it if it hasn't been set before. NOTE: We
1021 // cannot use GetProperty() to get the current value as it
1022 // 'unholes' the value.
1023 LookupResult lookup;
1024 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1025 ASSERT(lookup.IsProperty()); // the property was declared
1026 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1027
1028 PropertyType type = lookup.type();
1029 if (type == FIELD) {
1030 FixedArray* properties = context_ext->properties();
1031 int index = lookup.GetFieldIndex();
1032 if (properties->get(index)->IsTheHole()) {
1033 properties->set(index, *value);
1034 }
1035 } else if (type == NORMAL) {
1036 Dictionary* dictionary = context_ext->property_dictionary();
1037 int entry = lookup.GetDictionaryEntry();
1038 if (dictionary->ValueAt(entry)->IsTheHole()) {
1039 dictionary->ValueAtPut(entry, *value);
1040 }
1041 } else {
1042 // We should not reach here. Any real, named property should be
1043 // either a field or a dictionary slot.
1044 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001045 }
1046 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001047 // The property was found in a different context extension object.
1048 // Set it if it is not a read-only property.
1049 if ((attributes & READ_ONLY) == 0) {
1050 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1051 // Setting a property might throw an exception. Exceptions
1052 // are converted to empty handles in handle operations. We
1053 // need to convert back to exceptions here.
1054 if (set.is_null()) {
1055 ASSERT(Top::has_pending_exception());
1056 return Failure::Exception();
1057 }
1058 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001059 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001060
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001061 return *value;
1062}
1063
1064
1065static Object* Runtime_RegExpExec(Arguments args) {
1066 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001067 ASSERT(args.length() == 4);
ager@chromium.org236ad962008-09-25 09:45:57 +00001068 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
1069 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070 CONVERT_CHECKED(String, raw_subject, args[1]);
1071 Handle<String> subject(raw_subject);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001072 // Due to the way the JS files are constructed this must be less than the
1073 // length of a string, i.e. it is always a Smi. We check anyway for security.
1074 CONVERT_CHECKED(Smi, index, args[2]);
1075 CONVERT_CHECKED(JSArray, raw_last_match_info, args[3]);
1076 Handle<JSArray> last_match_info(raw_last_match_info);
ager@chromium.org41826e72009-03-30 13:30:57 +00001077 RUNTIME_ASSERT(last_match_info->HasFastElements());
1078 RUNTIME_ASSERT(index->value() >= 0);
1079 RUNTIME_ASSERT(index->value() <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001080 Handle<Object> result = RegExpImpl::Exec(regexp,
1081 subject,
1082 index->value(),
1083 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001084 if (result.is_null()) return Failure::Exception();
1085 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001086}
1087
1088
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001089static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1090 HandleScope scope;
1091 ASSERT(args.length() == 4);
1092 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1093 int index = Smi::cast(args[1])->value();
1094 Handle<String> pattern = args.at<String>(2);
1095 Handle<String> flags = args.at<String>(3);
1096
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001097 // Get the RegExp function from the context in the literals array.
1098 // This is the RegExp function from the context in which the
1099 // function was created. We do not use the RegExp function from the
1100 // current global context because this might be the RegExp function
1101 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001102 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001103 Handle<JSFunction>(
1104 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001105 // Compute the regular expression literal.
1106 bool has_pending_exception;
1107 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001108 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1109 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001110 if (has_pending_exception) {
1111 ASSERT(Top::has_pending_exception());
1112 return Failure::Exception();
1113 }
1114 literals->set(index, *regexp);
1115 return *regexp;
1116}
1117
1118
1119static Object* Runtime_FunctionGetName(Arguments args) {
1120 NoHandleAllocation ha;
1121 ASSERT(args.length() == 1);
1122
1123 CONVERT_CHECKED(JSFunction, f, args[0]);
1124 return f->shared()->name();
1125}
1126
1127
ager@chromium.org236ad962008-09-25 09:45:57 +00001128static Object* Runtime_FunctionSetName(Arguments args) {
1129 NoHandleAllocation ha;
1130 ASSERT(args.length() == 2);
1131
1132 CONVERT_CHECKED(JSFunction, f, args[0]);
1133 CONVERT_CHECKED(String, name, args[1]);
1134 f->shared()->set_name(name);
1135 return Heap::undefined_value();
1136}
1137
1138
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001139static Object* Runtime_FunctionGetScript(Arguments args) {
1140 HandleScope scope;
1141 ASSERT(args.length() == 1);
1142
1143 CONVERT_CHECKED(JSFunction, fun, args[0]);
1144 Handle<Object> script = Handle<Object>(fun->shared()->script());
1145 if (!script->IsScript()) return Heap::undefined_value();
1146
1147 return *GetScriptWrapper(Handle<Script>::cast(script));
1148}
1149
1150
1151static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1152 NoHandleAllocation ha;
1153 ASSERT(args.length() == 1);
1154
1155 CONVERT_CHECKED(JSFunction, f, args[0]);
1156 return f->shared()->GetSourceCode();
1157}
1158
1159
1160static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1161 NoHandleAllocation ha;
1162 ASSERT(args.length() == 1);
1163
1164 CONVERT_CHECKED(JSFunction, fun, args[0]);
1165 int pos = fun->shared()->start_position();
1166 return Smi::FromInt(pos);
1167}
1168
1169
1170static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1171 NoHandleAllocation ha;
1172 ASSERT(args.length() == 2);
1173
1174 CONVERT_CHECKED(JSFunction, fun, args[0]);
1175 CONVERT_CHECKED(String, name, args[1]);
1176 fun->SetInstanceClassName(name);
1177 return Heap::undefined_value();
1178}
1179
1180
1181static Object* Runtime_FunctionSetLength(Arguments args) {
1182 NoHandleAllocation ha;
1183 ASSERT(args.length() == 2);
1184
1185 CONVERT_CHECKED(JSFunction, fun, args[0]);
1186 CONVERT_CHECKED(Smi, length, args[1]);
1187 fun->shared()->set_length(length->value());
1188 return length;
1189}
1190
1191
1192static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001193 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001194 ASSERT(args.length() == 2);
1195
1196 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001197 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1198 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001199 return args[0]; // return TOS
1200}
1201
1202
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001203static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1204 NoHandleAllocation ha;
1205 ASSERT(args.length() == 1);
1206
1207 CONVERT_CHECKED(JSFunction, f, args[0]);
1208 // The function_data field of the shared function info is used exclusively by
1209 // the API.
1210 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1211 : Heap::false_value();
1212}
1213
1214
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001215static Object* Runtime_SetCode(Arguments args) {
1216 HandleScope scope;
1217 ASSERT(args.length() == 2);
1218
1219 CONVERT_CHECKED(JSFunction, raw_target, args[0]);
1220 Handle<JSFunction> target(raw_target);
1221 Handle<Object> code = args.at<Object>(1);
1222
1223 Handle<Context> context(target->context());
1224
1225 if (!code->IsNull()) {
1226 RUNTIME_ASSERT(code->IsJSFunction());
1227 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1228 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1229 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1230 return Failure::Exception();
1231 }
1232 // Set the code, formal parameter count, and the length of the target
1233 // function.
1234 target->set_code(fun->code());
1235 target->shared()->set_length(fun->shared()->length());
1236 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001237 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001238 // Set the source code of the target function to undefined.
1239 // SetCode is only used for built-in constructors like String,
1240 // Array, and Object, and some web code
1241 // doesn't like seeing source code for constructors.
1242 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001243 context = Handle<Context>(fun->context());
1244
1245 // Make sure we get a fresh copy of the literal vector to avoid
1246 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001247 int number_of_literals = fun->NumberOfLiterals();
1248 Handle<FixedArray> literals =
1249 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001250 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001251 // Insert the object, regexp and array functions in the literals
1252 // array prefix. These are the functions that will be used when
1253 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001254 literals->set(JSFunction::kLiteralGlobalContextIndex,
1255 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001256 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001257 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001258 }
1259
1260 target->set_context(*context);
1261 return *target;
1262}
1263
1264
1265static Object* CharCodeAt(String* subject, Object* index) {
1266 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001267 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001268 // Flatten the string. If someone wants to get a char at an index
1269 // in a cons string, it is likely that more indices will be
1270 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001271 subject->TryFlattenIfNotFlat();
1272 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001273 return Heap::nan_value();
1274 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001275 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001276}
1277
1278
1279static Object* Runtime_StringCharCodeAt(Arguments args) {
1280 NoHandleAllocation ha;
1281 ASSERT(args.length() == 2);
1282
1283 CONVERT_CHECKED(String, subject, args[0]);
1284 Object* index = args[1];
1285 return CharCodeAt(subject, index);
1286}
1287
1288
1289static Object* Runtime_CharFromCode(Arguments args) {
1290 NoHandleAllocation ha;
1291 ASSERT(args.length() == 1);
1292 uint32_t code;
1293 if (Array::IndexFromObject(args[0], &code)) {
1294 if (code <= 0xffff) {
1295 return Heap::LookupSingleCharacterStringFromCode(code);
1296 }
1297 }
1298 return Heap::empty_string();
1299}
1300
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001301// Forward declarations.
1302static const int kStringBuilderConcatHelperLengthBits = 11;
1303static const int kStringBuilderConcatHelperPositionBits = 19;
1304
1305template <typename schar>
1306static inline void StringBuilderConcatHelper(String*,
1307 schar*,
1308 FixedArray*,
1309 int);
1310
1311typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1312typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1313
1314class ReplacementStringBuilder {
1315 public:
1316 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1317 : subject_(subject),
1318 parts_(Factory::NewFixedArray(estimated_part_count)),
1319 part_count_(0),
1320 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001321 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001322 // Require a non-zero initial size. Ensures that doubling the size to
1323 // extend the array will work.
1324 ASSERT(estimated_part_count > 0);
1325 }
1326
1327 void EnsureCapacity(int elements) {
1328 int length = parts_->length();
1329 int required_length = part_count_ + elements;
1330 if (length < required_length) {
1331 int new_length = length;
1332 do {
1333 new_length *= 2;
1334 } while (new_length < required_length);
1335 Handle<FixedArray> extended_array =
1336 Factory::NewFixedArray(new_length);
1337 parts_->CopyTo(0, *extended_array, 0, part_count_);
1338 parts_ = extended_array;
1339 }
1340 }
1341
1342 void AddSubjectSlice(int from, int to) {
1343 ASSERT(from >= 0);
1344 int length = to - from;
1345 ASSERT(length > 0);
1346 // Can we encode the slice in 11 bits for length and 19 bits for
1347 // start position - as used by StringBuilderConcatHelper?
1348 if (StringBuilderSubstringLength::is_valid(length) &&
1349 StringBuilderSubstringPosition::is_valid(from)) {
1350 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1351 StringBuilderSubstringPosition::encode(from);
1352 AddElement(Smi::FromInt(encoded_slice));
1353 } else {
1354 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1355 AddElement(*slice);
1356 }
1357 IncrementCharacterCount(length);
1358 }
1359
1360
1361 void AddString(Handle<String> string) {
1362 int length = string->length();
1363 ASSERT(length > 0);
1364 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001365 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001366 is_ascii_ = false;
1367 }
1368 IncrementCharacterCount(length);
1369 }
1370
1371
1372 Handle<String> ToString() {
1373 if (part_count_ == 0) {
1374 return Factory::empty_string();
1375 }
1376
1377 Handle<String> joined_string;
1378 if (is_ascii_) {
1379 joined_string = NewRawAsciiString(character_count_);
1380 AssertNoAllocation no_alloc;
1381 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1382 char* char_buffer = seq->GetChars();
1383 StringBuilderConcatHelper(*subject_,
1384 char_buffer,
1385 *parts_,
1386 part_count_);
1387 } else {
1388 // Non-ASCII.
1389 joined_string = NewRawTwoByteString(character_count_);
1390 AssertNoAllocation no_alloc;
1391 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1392 uc16* char_buffer = seq->GetChars();
1393 StringBuilderConcatHelper(*subject_,
1394 char_buffer,
1395 *parts_,
1396 part_count_);
1397 }
1398 return joined_string;
1399 }
1400
1401
1402 void IncrementCharacterCount(int by) {
1403 if (character_count_ > Smi::kMaxValue - by) {
1404 V8::FatalProcessOutOfMemory("String.replace result too large.");
1405 }
1406 character_count_ += by;
1407 }
1408
1409 private:
1410
1411 Handle<String> NewRawAsciiString(int size) {
1412 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1413 }
1414
1415
1416 Handle<String> NewRawTwoByteString(int size) {
1417 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1418 }
1419
1420
1421 void AddElement(Object* element) {
1422 ASSERT(element->IsSmi() || element->IsString());
1423 parts_->set(part_count_, element);
1424 part_count_++;
1425 }
1426
1427 Handle<String> subject_;
1428 Handle<FixedArray> parts_;
1429 int part_count_;
1430 int character_count_;
1431 bool is_ascii_;
1432};
1433
1434
1435class CompiledReplacement {
1436 public:
1437 CompiledReplacement()
1438 : parts_(1), replacement_substrings_(0) {}
1439
1440 void Compile(Handle<String> replacement,
1441 int capture_count,
1442 int subject_length);
1443
1444 void Apply(ReplacementStringBuilder* builder,
1445 int match_from,
1446 int match_to,
1447 Handle<JSArray> last_match_info);
1448
1449 // Number of distinct parts of the replacement pattern.
1450 int parts() {
1451 return parts_.length();
1452 }
1453 private:
1454 enum PartType {
1455 SUBJECT_PREFIX = 1,
1456 SUBJECT_SUFFIX,
1457 SUBJECT_CAPTURE,
1458 REPLACEMENT_SUBSTRING,
1459 REPLACEMENT_STRING,
1460
1461 NUMBER_OF_PART_TYPES
1462 };
1463
1464 struct ReplacementPart {
1465 static inline ReplacementPart SubjectMatch() {
1466 return ReplacementPart(SUBJECT_CAPTURE, 0);
1467 }
1468 static inline ReplacementPart SubjectCapture(int capture_index) {
1469 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1470 }
1471 static inline ReplacementPart SubjectPrefix() {
1472 return ReplacementPart(SUBJECT_PREFIX, 0);
1473 }
1474 static inline ReplacementPart SubjectSuffix(int subject_length) {
1475 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1476 }
1477 static inline ReplacementPart ReplacementString() {
1478 return ReplacementPart(REPLACEMENT_STRING, 0);
1479 }
1480 static inline ReplacementPart ReplacementSubString(int from, int to) {
1481 ASSERT(from >= 0);
1482 ASSERT(to > from);
1483 return ReplacementPart(-from, to);
1484 }
1485
1486 // If tag <= 0 then it is the negation of a start index of a substring of
1487 // the replacement pattern, otherwise it's a value from PartType.
1488 ReplacementPart(int tag, int data)
1489 : tag(tag), data(data) {
1490 // Must be non-positive or a PartType value.
1491 ASSERT(tag < NUMBER_OF_PART_TYPES);
1492 }
1493 // Either a value of PartType or a non-positive number that is
1494 // the negation of an index into the replacement string.
1495 int tag;
1496 // The data value's interpretation depends on the value of tag:
1497 // tag == SUBJECT_PREFIX ||
1498 // tag == SUBJECT_SUFFIX: data is unused.
1499 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1500 // tag == REPLACEMENT_SUBSTRING ||
1501 // tag == REPLACEMENT_STRING: data is index into array of substrings
1502 // of the replacement string.
1503 // tag <= 0: Temporary representation of the substring of the replacement
1504 // string ranging over -tag .. data.
1505 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1506 // substring objects.
1507 int data;
1508 };
1509
1510 template<typename Char>
1511 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1512 Vector<Char> characters,
1513 int capture_count,
1514 int subject_length) {
1515 int length = characters.length();
1516 int last = 0;
1517 for (int i = 0; i < length; i++) {
1518 Char c = characters[i];
1519 if (c == '$') {
1520 int next_index = i + 1;
1521 if (next_index == length) { // No next character!
1522 break;
1523 }
1524 Char c2 = characters[next_index];
1525 switch (c2) {
1526 case '$':
1527 if (i > last) {
1528 // There is a substring before. Include the first "$".
1529 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1530 last = next_index + 1; // Continue after the second "$".
1531 } else {
1532 // Let the next substring start with the second "$".
1533 last = next_index;
1534 }
1535 i = next_index;
1536 break;
1537 case '`':
1538 if (i > last) {
1539 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1540 }
1541 parts->Add(ReplacementPart::SubjectPrefix());
1542 i = next_index;
1543 last = i + 1;
1544 break;
1545 case '\'':
1546 if (i > last) {
1547 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1548 }
1549 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1550 i = next_index;
1551 last = i + 1;
1552 break;
1553 case '&':
1554 if (i > last) {
1555 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1556 }
1557 parts->Add(ReplacementPart::SubjectMatch());
1558 i = next_index;
1559 last = i + 1;
1560 break;
1561 case '0':
1562 case '1':
1563 case '2':
1564 case '3':
1565 case '4':
1566 case '5':
1567 case '6':
1568 case '7':
1569 case '8':
1570 case '9': {
1571 int capture_ref = c2 - '0';
1572 if (capture_ref > capture_count) {
1573 i = next_index;
1574 continue;
1575 }
1576 int second_digit_index = next_index + 1;
1577 if (second_digit_index < length) {
1578 // Peek ahead to see if we have two digits.
1579 Char c3 = characters[second_digit_index];
1580 if ('0' <= c3 && c3 <= '9') { // Double digits.
1581 int double_digit_ref = capture_ref * 10 + c3 - '0';
1582 if (double_digit_ref <= capture_count) {
1583 next_index = second_digit_index;
1584 capture_ref = double_digit_ref;
1585 }
1586 }
1587 }
1588 if (capture_ref > 0) {
1589 if (i > last) {
1590 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1591 }
1592 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1593 last = next_index + 1;
1594 }
1595 i = next_index;
1596 break;
1597 }
1598 default:
1599 i = next_index;
1600 break;
1601 }
1602 }
1603 }
1604 if (length > last) {
1605 if (last == 0) {
1606 parts->Add(ReplacementPart::ReplacementString());
1607 } else {
1608 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1609 }
1610 }
1611 }
1612
1613 ZoneList<ReplacementPart> parts_;
1614 ZoneList<Handle<String> > replacement_substrings_;
1615};
1616
1617
1618void CompiledReplacement::Compile(Handle<String> replacement,
1619 int capture_count,
1620 int subject_length) {
1621 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001622 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001623 AssertNoAllocation no_alloc;
1624 ParseReplacementPattern(&parts_,
1625 replacement->ToAsciiVector(),
1626 capture_count,
1627 subject_length);
1628 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001629 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001630 AssertNoAllocation no_alloc;
1631
1632 ParseReplacementPattern(&parts_,
1633 replacement->ToUC16Vector(),
1634 capture_count,
1635 subject_length);
1636 }
1637 // Find substrings of replacement string and create them as String objects..
1638 int substring_index = 0;
1639 for (int i = 0, n = parts_.length(); i < n; i++) {
1640 int tag = parts_[i].tag;
1641 if (tag <= 0) { // A replacement string slice.
1642 int from = -tag;
1643 int to = parts_[i].data;
1644 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1645 from,
1646 to));
1647 parts_[i].tag = REPLACEMENT_SUBSTRING;
1648 parts_[i].data = substring_index;
1649 substring_index++;
1650 } else if (tag == REPLACEMENT_STRING) {
1651 replacement_substrings_.Add(replacement);
1652 parts_[i].data = substring_index;
1653 substring_index++;
1654 }
1655 }
1656}
1657
1658
1659void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1660 int match_from,
1661 int match_to,
1662 Handle<JSArray> last_match_info) {
1663 for (int i = 0, n = parts_.length(); i < n; i++) {
1664 ReplacementPart part = parts_[i];
1665 switch (part.tag) {
1666 case SUBJECT_PREFIX:
1667 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1668 break;
1669 case SUBJECT_SUFFIX: {
1670 int subject_length = part.data;
1671 if (match_to < subject_length) {
1672 builder->AddSubjectSlice(match_to, subject_length);
1673 }
1674 break;
1675 }
1676 case SUBJECT_CAPTURE: {
1677 int capture = part.data;
1678 FixedArray* match_info = last_match_info->elements();
1679 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1680 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1681 if (from >= 0 && to > from) {
1682 builder->AddSubjectSlice(from, to);
1683 }
1684 break;
1685 }
1686 case REPLACEMENT_SUBSTRING:
1687 case REPLACEMENT_STRING:
1688 builder->AddString(replacement_substrings_[part.data]);
1689 break;
1690 default:
1691 UNREACHABLE();
1692 }
1693 }
1694}
1695
1696
1697
1698static Object* StringReplaceRegExpWithString(String* subject,
1699 JSRegExp* regexp,
1700 String* replacement,
1701 JSArray* last_match_info) {
1702 ASSERT(subject->IsFlat());
1703 ASSERT(replacement->IsFlat());
1704
1705 HandleScope handles;
1706
1707 int length = subject->length();
1708 Handle<String> subject_handle(subject);
1709 Handle<JSRegExp> regexp_handle(regexp);
1710 Handle<String> replacement_handle(replacement);
1711 Handle<JSArray> last_match_info_handle(last_match_info);
1712 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1713 subject_handle,
1714 0,
1715 last_match_info_handle);
1716 if (match.is_null()) {
1717 return Failure::Exception();
1718 }
1719 if (match->IsNull()) {
1720 return *subject_handle;
1721 }
1722
1723 int capture_count = regexp_handle->CaptureCount();
1724
1725 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001726 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001727 CompiledReplacement compiled_replacement;
1728 compiled_replacement.Compile(replacement_handle,
1729 capture_count,
1730 length);
1731
1732 bool is_global = regexp_handle->GetFlags().is_global();
1733
1734 // Guessing the number of parts that the final result string is built
1735 // from. Global regexps can match any number of times, so we guess
1736 // conservatively.
1737 int expected_parts =
1738 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1739 ReplacementStringBuilder builder(subject_handle, expected_parts);
1740
1741 // Index of end of last match.
1742 int prev = 0;
1743
1744 // Number of parts added by compiled replacement plus preceeding string
1745 // and possibly suffix after last match.
1746 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1747 bool matched = true;
1748 do {
1749 ASSERT(last_match_info_handle->HasFastElements());
1750 // Increase the capacity of the builder before entering local handle-scope,
1751 // so its internal buffer can safely allocate a new handle if it grows.
1752 builder.EnsureCapacity(parts_added_per_loop);
1753
1754 HandleScope loop_scope;
1755 int start, end;
1756 {
1757 AssertNoAllocation match_info_array_is_not_in_a_handle;
1758 FixedArray* match_info_array = last_match_info_handle->elements();
1759
1760 ASSERT_EQ(capture_count * 2 + 2,
1761 RegExpImpl::GetLastCaptureCount(match_info_array));
1762 start = RegExpImpl::GetCapture(match_info_array, 0);
1763 end = RegExpImpl::GetCapture(match_info_array, 1);
1764 }
1765
1766 if (prev < start) {
1767 builder.AddSubjectSlice(prev, start);
1768 }
1769 compiled_replacement.Apply(&builder,
1770 start,
1771 end,
1772 last_match_info_handle);
1773 prev = end;
1774
1775 // Only continue checking for global regexps.
1776 if (!is_global) break;
1777
1778 // Continue from where the match ended, unless it was an empty match.
1779 int next = end;
1780 if (start == end) {
1781 next = end + 1;
1782 if (next > length) break;
1783 }
1784
1785 match = RegExpImpl::Exec(regexp_handle,
1786 subject_handle,
1787 next,
1788 last_match_info_handle);
1789 if (match.is_null()) {
1790 return Failure::Exception();
1791 }
1792 matched = !match->IsNull();
1793 } while (matched);
1794
1795 if (prev < length) {
1796 builder.AddSubjectSlice(prev, length);
1797 }
1798
1799 return *(builder.ToString());
1800}
1801
1802
1803static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1804 ASSERT(args.length() == 4);
1805
1806 CONVERT_CHECKED(String, subject, args[0]);
1807 if (!subject->IsFlat()) {
1808 Object* flat_subject = subject->TryFlatten();
1809 if (flat_subject->IsFailure()) {
1810 return flat_subject;
1811 }
1812 subject = String::cast(flat_subject);
1813 }
1814
1815 CONVERT_CHECKED(String, replacement, args[2]);
1816 if (!replacement->IsFlat()) {
1817 Object* flat_replacement = replacement->TryFlatten();
1818 if (flat_replacement->IsFailure()) {
1819 return flat_replacement;
1820 }
1821 replacement = String::cast(flat_replacement);
1822 }
1823
1824 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1825 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1826
1827 ASSERT(last_match_info->HasFastElements());
1828
1829 return StringReplaceRegExpWithString(subject,
1830 regexp,
1831 replacement,
1832 last_match_info);
1833}
1834
1835
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001836
ager@chromium.org7c537e22008-10-16 08:43:32 +00001837// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1838// limit, we can fix the size of tables.
1839static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001840// Reduce alphabet to this size.
1841static const int kBMAlphabetSize = 0x100;
1842// For patterns below this length, the skip length of Boyer-Moore is too short
1843// to compensate for the algorithmic overhead compared to simple brute force.
1844static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001845
ager@chromium.org7c537e22008-10-16 08:43:32 +00001846// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1847// shift. Only allows the last kBMMaxShift characters of the needle
1848// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001849class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001850 public:
1851 BMGoodSuffixBuffers() {}
1852 inline void init(int needle_length) {
1853 ASSERT(needle_length > 1);
1854 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1855 int len = needle_length - start;
1856 biased_suffixes_ = suffixes_ - start;
1857 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1858 for (int i = 0; i <= len; i++) {
1859 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001860 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001861 }
1862 inline int& suffix(int index) {
1863 ASSERT(biased_suffixes_ + index >= suffixes_);
1864 return biased_suffixes_[index];
1865 }
1866 inline int& shift(int index) {
1867 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1868 return biased_good_suffix_shift_[index];
1869 }
1870 private:
1871 int suffixes_[kBMMaxShift + 1];
1872 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001873 int* biased_suffixes_;
1874 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001875 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1876};
1877
1878// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001879static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001880static BMGoodSuffixBuffers bmgs_buffers;
1881
1882// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001883template <typename pchar>
1884static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1885 int start) {
1886 // Run forwards to populate bad_char_table, so that *last* instance
1887 // of character equivalence class is the one registered.
1888 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001889 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1890 : kBMAlphabetSize;
1891 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001892 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001893 } else {
1894 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001895 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001896 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001897 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001898 for (int i = start; i < pattern.length() - 1; i++) {
1899 pchar c = pattern[i];
1900 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001901 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001902 }
1903}
1904
1905template <typename pchar>
1906static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001907 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001908 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001909 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001910 // Compute Good Suffix tables.
1911 bmgs_buffers.init(m);
1912
1913 bmgs_buffers.shift(m-1) = 1;
1914 bmgs_buffers.suffix(m) = m + 1;
1915 pchar last_char = pattern[m - 1];
1916 int suffix = m + 1;
1917 for (int i = m; i > start;) {
1918 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1919 if (bmgs_buffers.shift(suffix) == len) {
1920 bmgs_buffers.shift(suffix) = suffix - i;
1921 }
1922 suffix = bmgs_buffers.suffix(suffix);
1923 }
1924 i--;
1925 suffix--;
1926 bmgs_buffers.suffix(i) = suffix;
1927 if (suffix == m) {
1928 // No suffix to extend, so we check against last_char only.
1929 while (i > start && pattern[i - 1] != last_char) {
1930 if (bmgs_buffers.shift(m) == len) {
1931 bmgs_buffers.shift(m) = m - i;
1932 }
1933 i--;
1934 bmgs_buffers.suffix(i) = m;
1935 }
1936 if (i > start) {
1937 i--;
1938 suffix--;
1939 bmgs_buffers.suffix(i) = suffix;
1940 }
1941 }
1942 }
1943 if (suffix < m) {
1944 for (int i = start; i <= m; i++) {
1945 if (bmgs_buffers.shift(i) == len) {
1946 bmgs_buffers.shift(i) = suffix - start;
1947 }
1948 if (i == suffix) {
1949 suffix = bmgs_buffers.suffix(suffix);
1950 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001951 }
1952 }
1953}
1954
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001955template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001956static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001957 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001958 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001959 }
1960 if (sizeof(pchar) == 1) {
1961 if (char_code > String::kMaxAsciiCharCode) {
1962 return -1;
1963 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001964 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001965 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001966 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001967}
1968
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001969// Restricted simplified Boyer-Moore string matching.
1970// Uses only the bad-shift table of Boyer-Moore and only uses it
1971// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001972template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001973static int BoyerMooreHorspool(Vector<const schar> subject,
1974 Vector<const pchar> pattern,
1975 int start_index,
1976 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001977 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001978 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001979 // Only preprocess at most kBMMaxShift last characters of pattern.
1980 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001981
ager@chromium.org7c537e22008-10-16 08:43:32 +00001982 BoyerMoorePopulateBadCharTable(pattern, start);
1983
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001984 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001985 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001986 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001987 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001988 // Perform search
1989 for (idx = start_index; idx <= n - m;) {
1990 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001991 int c;
1992 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001993 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001994 int shift = j - bc_occ;
1995 idx += shift;
1996 badness += 1 - shift; // at most zero, so badness cannot increase.
1997 if (idx > n - m) {
1998 *complete = true;
1999 return -1;
2000 }
2001 }
2002 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002003 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002004 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002005 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002006 return idx;
2007 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002008 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002009 // Badness increases by the number of characters we have
2010 // checked, and decreases by the number of characters we
2011 // can skip by shifting. It's a measure of how we are doing
2012 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002013 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002014 if (badness > 0) {
2015 *complete = false;
2016 return idx;
2017 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002018 }
2019 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002020 *complete = true;
2021 return -1;
2022}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002023
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002024
2025template <typename schar, typename pchar>
2026static int BoyerMooreIndexOf(Vector<const schar> subject,
2027 Vector<const pchar> pattern,
2028 int idx) {
2029 int n = subject.length();
2030 int m = pattern.length();
2031 // Only preprocess at most kBMMaxShift last characters of pattern.
2032 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2033
2034 // Build the Good Suffix table and continue searching.
2035 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2036 pchar last_char = pattern[m - 1];
2037 // Continue search from i.
2038 do {
2039 int j = m - 1;
2040 schar c;
2041 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002042 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002043 idx += shift;
2044 if (idx > n - m) {
2045 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002046 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002047 }
2048 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2049 if (j < 0) {
2050 return idx;
2051 } else if (j < start) {
2052 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002053 // Fall back on BMH shift.
2054 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002055 } else {
2056 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002057 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002058 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002059 if (gs_shift > shift) {
2060 shift = gs_shift;
2061 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002062 idx += shift;
2063 }
2064 } while (idx <= n - m);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002065
2066 return -1;
2067}
2068
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002069
2070template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002071static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002072 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002073 int start_index) {
2074 for (int i = start_index, n = string.length(); i < n; i++) {
2075 if (pattern_char == string[i]) {
2076 return i;
2077 }
2078 }
2079 return -1;
2080}
2081
2082// Trivial string search for shorter strings.
2083// On return, if "complete" is set to true, the return value is the
2084// final result of searching for the patter in the subject.
2085// If "complete" is set to false, the return value is the index where
2086// further checking should start, i.e., it's guaranteed that the pattern
2087// does not occur at a position prior to the returned index.
2088template <typename pchar, typename schar>
2089static int SimpleIndexOf(Vector<const schar> subject,
2090 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002091 int idx,
2092 bool* complete) {
2093 // Badness is a count of how much work we have done. When we have
2094 // done enough work we decide it's probably worth switching to a better
2095 // algorithm.
2096 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002097 // We know our pattern is at least 2 characters, we cache the first so
2098 // the common case of the first character not matching is faster.
2099 pchar pattern_first_char = pattern[0];
2100
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002101 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2102 badness++;
2103 if (badness > 0) {
2104 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002105 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002106 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002107 if (subject[i] != pattern_first_char) continue;
2108 int j = 1;
2109 do {
2110 if (pattern[j] != subject[i+j]) {
2111 break;
2112 }
2113 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002114 } while (j < pattern.length());
2115 if (j == pattern.length()) {
2116 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002117 return i;
2118 }
2119 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002120 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002121 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002122 return -1;
2123}
2124
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002125// Simple indexOf that never bails out. For short patterns only.
2126template <typename pchar, typename schar>
2127static int SimpleIndexOf(Vector<const schar> subject,
2128 Vector<const pchar> pattern,
2129 int idx) {
2130 pchar pattern_first_char = pattern[0];
2131 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2132 if (subject[i] != pattern_first_char) continue;
2133 int j = 1;
2134 do {
2135 if (pattern[j] != subject[i+j]) {
2136 break;
2137 }
2138 j++;
2139 } while (j < pattern.length());
2140 if (j == pattern.length()) {
2141 return i;
2142 }
2143 }
2144 return -1;
2145}
2146
2147
2148// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002149template <typename schar, typename pchar>
2150static int StringMatchStrategy(Vector<const schar> sub,
2151 Vector<const pchar> pat,
2152 int start_index) {
2153 ASSERT(pat.length() > 1);
2154
2155 // We have an ASCII haystack and a non-ASCII needle. Check if there
2156 // really is a non-ASCII character in the needle and bail out if there
2157 // is.
2158 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2159 for (int i = 0; i < pat.length(); i++) {
2160 uc16 c = pat[i];
2161 if (c > String::kMaxAsciiCharCode) {
2162 return -1;
2163 }
2164 }
2165 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002166 if (pat.length() < kBMMinPatternLength) {
2167 // We don't believe fancy searching can ever be more efficient.
2168 // The max shift of Boyer-Moore on a pattern of this length does
2169 // not compensate for the overhead.
2170 return SimpleIndexOf(sub, pat, start_index);
2171 }
2172 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002173 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002174 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2175 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002176 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002177 if (complete) return idx;
2178 return BoyerMooreIndexOf(sub, pat, idx);
2179}
2180
2181// Perform string match of pattern on subject, starting at start index.
2182// Caller must ensure that 0 <= start_index <= sub->length(),
2183// and should check that pat->length() + start_index <= sub->length()
2184int Runtime::StringMatch(Handle<String> sub,
2185 Handle<String> pat,
2186 int start_index) {
2187 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002188 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002189
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002190 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002191 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002192
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002193 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002194 if (start_index + pattern_length > subject_length) return -1;
2195
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002196 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002197 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002198 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002199 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002200 // character patterns linear search is necessary, so any smart
2201 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002202 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002203 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002204 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002205 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002206 if (pchar > String::kMaxAsciiCharCode) {
2207 return -1;
2208 }
2209 Vector<const char> ascii_vector =
2210 sub->ToAsciiVector().SubVector(start_index, subject_length);
2211 const void* pos = memchr(ascii_vector.start(),
2212 static_cast<const char>(pchar),
2213 static_cast<size_t>(ascii_vector.length()));
2214 if (pos == NULL) {
2215 return -1;
2216 }
2217 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2218 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002219 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002220 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002221 }
2222
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002223 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002224 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002225 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002226
ager@chromium.org7c537e22008-10-16 08:43:32 +00002227 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2228 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002229 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002230 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002231 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002232 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002233 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002234 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002235 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002236 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002237 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002238 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002239 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002240 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002241}
2242
2243
2244static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002245 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002246 ASSERT(args.length() == 3);
2247
ager@chromium.org7c537e22008-10-16 08:43:32 +00002248 CONVERT_ARG_CHECKED(String, sub, 0);
2249 CONVERT_ARG_CHECKED(String, pat, 1);
2250
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002251 Object* index = args[2];
2252 uint32_t start_index;
2253 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2254
ager@chromium.org870a0b62008-11-04 11:43:05 +00002255 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002256 int position = Runtime::StringMatch(sub, pat, start_index);
2257 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002258}
2259
2260
2261static Object* Runtime_StringLastIndexOf(Arguments args) {
2262 NoHandleAllocation ha;
2263 ASSERT(args.length() == 3);
2264
2265 CONVERT_CHECKED(String, sub, args[0]);
2266 CONVERT_CHECKED(String, pat, args[1]);
2267 Object* index = args[2];
2268
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002269 sub->TryFlattenIfNotFlat();
2270 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002271
2272 uint32_t start_index;
2273 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2274
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002275 uint32_t pattern_length = pat->length();
2276 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002277
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002278 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002279 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002280 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002281
2282 for (int i = start_index; i >= 0; i--) {
2283 bool found = true;
2284 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002285 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002286 found = false;
2287 break;
2288 }
2289 }
2290 if (found) return Smi::FromInt(i);
2291 }
2292
2293 return Smi::FromInt(-1);
2294}
2295
2296
2297static Object* Runtime_StringLocaleCompare(Arguments args) {
2298 NoHandleAllocation ha;
2299 ASSERT(args.length() == 2);
2300
2301 CONVERT_CHECKED(String, str1, args[0]);
2302 CONVERT_CHECKED(String, str2, args[1]);
2303
2304 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002305 int str1_length = str1->length();
2306 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002307
2308 // Decide trivial cases without flattening.
2309 if (str1_length == 0) {
2310 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2311 return Smi::FromInt(-str2_length);
2312 } else {
2313 if (str2_length == 0) return Smi::FromInt(str1_length);
2314 }
2315
2316 int end = str1_length < str2_length ? str1_length : str2_length;
2317
2318 // No need to flatten if we are going to find the answer on the first
2319 // character. At this point we know there is at least one character
2320 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002321 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002322 if (d != 0) return Smi::FromInt(d);
2323
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002324 str1->TryFlattenIfNotFlat();
2325 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002326
2327 static StringInputBuffer buf1;
2328 static StringInputBuffer buf2;
2329
2330 buf1.Reset(str1);
2331 buf2.Reset(str2);
2332
2333 for (int i = 0; i < end; i++) {
2334 uint16_t char1 = buf1.GetNext();
2335 uint16_t char2 = buf2.GetNext();
2336 if (char1 != char2) return Smi::FromInt(char1 - char2);
2337 }
2338
2339 return Smi::FromInt(str1_length - str2_length);
2340}
2341
2342
2343static Object* Runtime_StringSlice(Arguments args) {
2344 NoHandleAllocation ha;
2345 ASSERT(args.length() == 3);
2346
2347 CONVERT_CHECKED(String, value, args[0]);
2348 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2349 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2350
2351 int start = FastD2I(from_number);
2352 int end = FastD2I(to_number);
2353
2354 RUNTIME_ASSERT(end >= start);
2355 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002356 RUNTIME_ASSERT(end <= value->length());
2357 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002358}
2359
2360
ager@chromium.org41826e72009-03-30 13:30:57 +00002361static Object* Runtime_StringMatch(Arguments args) {
2362 ASSERT_EQ(3, args.length());
2363
2364 CONVERT_ARG_CHECKED(String, subject, 0);
2365 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2366 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2367 HandleScope handles;
2368
2369 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2370
2371 if (match.is_null()) {
2372 return Failure::Exception();
2373 }
2374 if (match->IsNull()) {
2375 return Heap::null_value();
2376 }
2377 int length = subject->length();
2378
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002379 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002380 ZoneList<int> offsets(8);
2381 do {
2382 int start;
2383 int end;
2384 {
2385 AssertNoAllocation no_alloc;
2386 FixedArray* elements = regexp_info->elements();
2387 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2388 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2389 }
2390 offsets.Add(start);
2391 offsets.Add(end);
2392 int index = start < end ? end : end + 1;
2393 if (index > length) break;
2394 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2395 if (match.is_null()) {
2396 return Failure::Exception();
2397 }
2398 } while (!match->IsNull());
2399 int matches = offsets.length() / 2;
2400 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2401 for (int i = 0; i < matches ; i++) {
2402 int from = offsets.at(i * 2);
2403 int to = offsets.at(i * 2 + 1);
2404 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2405 }
2406 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2407 result->set_length(Smi::FromInt(matches));
2408 return *result;
2409}
2410
2411
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002412static Object* Runtime_NumberToRadixString(Arguments args) {
2413 NoHandleAllocation ha;
2414 ASSERT(args.length() == 2);
2415
2416 CONVERT_DOUBLE_CHECKED(value, args[0]);
2417 if (isnan(value)) {
2418 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2419 }
2420 if (isinf(value)) {
2421 if (value < 0) {
2422 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2423 }
2424 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2425 }
2426 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2427 int radix = FastD2I(radix_number);
2428 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2429 char* str = DoubleToRadixCString(value, radix);
2430 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2431 DeleteArray(str);
2432 return result;
2433}
2434
2435
2436static Object* Runtime_NumberToFixed(Arguments args) {
2437 NoHandleAllocation ha;
2438 ASSERT(args.length() == 2);
2439
2440 CONVERT_DOUBLE_CHECKED(value, args[0]);
2441 if (isnan(value)) {
2442 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2443 }
2444 if (isinf(value)) {
2445 if (value < 0) {
2446 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2447 }
2448 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2449 }
2450 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2451 int f = FastD2I(f_number);
2452 RUNTIME_ASSERT(f >= 0);
2453 char* str = DoubleToFixedCString(value, f);
2454 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2455 DeleteArray(str);
2456 return res;
2457}
2458
2459
2460static Object* Runtime_NumberToExponential(Arguments args) {
2461 NoHandleAllocation ha;
2462 ASSERT(args.length() == 2);
2463
2464 CONVERT_DOUBLE_CHECKED(value, args[0]);
2465 if (isnan(value)) {
2466 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2467 }
2468 if (isinf(value)) {
2469 if (value < 0) {
2470 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2471 }
2472 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2473 }
2474 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2475 int f = FastD2I(f_number);
2476 RUNTIME_ASSERT(f >= -1 && f <= 20);
2477 char* str = DoubleToExponentialCString(value, f);
2478 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2479 DeleteArray(str);
2480 return res;
2481}
2482
2483
2484static Object* Runtime_NumberToPrecision(Arguments args) {
2485 NoHandleAllocation ha;
2486 ASSERT(args.length() == 2);
2487
2488 CONVERT_DOUBLE_CHECKED(value, args[0]);
2489 if (isnan(value)) {
2490 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2491 }
2492 if (isinf(value)) {
2493 if (value < 0) {
2494 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2495 }
2496 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2497 }
2498 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2499 int f = FastD2I(f_number);
2500 RUNTIME_ASSERT(f >= 1 && f <= 21);
2501 char* str = DoubleToPrecisionCString(value, f);
2502 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2503 DeleteArray(str);
2504 return res;
2505}
2506
2507
2508// Returns a single character string where first character equals
2509// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002510static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002511 if (index < static_cast<uint32_t>(string->length())) {
2512 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002513 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002514 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002515 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002516 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002517}
2518
2519
2520Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2521 // Handle [] indexing on Strings
2522 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002523 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2524 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002525 }
2526
2527 // Handle [] indexing on String objects
2528 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002529 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2530 Handle<Object> result =
2531 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2532 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002533 }
2534
2535 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002536 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537 return prototype->GetElement(index);
2538 }
2539
2540 return object->GetElement(index);
2541}
2542
2543
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002544Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2545 HandleScope scope;
2546
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002547 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002548 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549 Handle<Object> error =
2550 Factory::NewTypeError("non_object_property_load",
2551 HandleVector(args, 2));
2552 return Top::Throw(*error);
2553 }
2554
2555 // Check if the given key is an array index.
2556 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002557 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002558 return GetElementOrCharAt(object, index);
2559 }
2560
2561 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002562 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002563 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002564 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002565 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002566 bool has_pending_exception = false;
2567 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002568 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002570 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002571 }
2572
ager@chromium.org32912102009-01-16 10:38:43 +00002573 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002574 // the element if so.
2575 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002576 return GetElementOrCharAt(object, index);
2577 } else {
2578 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002579 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002580 }
2581}
2582
2583
2584static Object* Runtime_GetProperty(Arguments args) {
2585 NoHandleAllocation ha;
2586 ASSERT(args.length() == 2);
2587
2588 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002589 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590
2591 return Runtime::GetObjectProperty(object, key);
2592}
2593
2594
ager@chromium.org7c537e22008-10-16 08:43:32 +00002595
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002596// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002597static Object* Runtime_KeyedGetProperty(Arguments args) {
2598 NoHandleAllocation ha;
2599 ASSERT(args.length() == 2);
2600
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002601 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002602 // itself.
2603 //
2604 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002605 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002606 // global proxy object never has properties. This is the case
2607 // because the global proxy object forwards everything to its hidden
2608 // prototype including local lookups.
2609 //
2610 // Additionally, we need to make sure that we do not cache results
2611 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002612 if (args[0]->IsJSObject() &&
2613 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002614 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 args[1]->IsString()) {
2616 JSObject* receiver = JSObject::cast(args[0]);
2617 String* key = String::cast(args[1]);
2618 if (receiver->HasFastProperties()) {
2619 // Attempt to use lookup cache.
2620 Object* obj = Heap::GetKeyedLookupCache();
2621 if (obj->IsFailure()) return obj;
2622 LookupCache* cache = LookupCache::cast(obj);
2623 Map* receiver_map = receiver->map();
2624 int offset = cache->Lookup(receiver_map, key);
2625 if (offset != LookupCache::kNotFound) {
2626 Object* value = receiver->FastPropertyAt(offset);
2627 return value->IsTheHole() ? Heap::undefined_value() : value;
2628 }
2629 // Lookup cache miss. Perform lookup and update the cache if
2630 // appropriate.
2631 LookupResult result;
2632 receiver->LocalLookup(key, &result);
2633 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2634 int offset = result.GetFieldIndex();
2635 Object* obj = cache->Put(receiver_map, key, offset);
2636 if (obj->IsFailure()) return obj;
2637 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
2638 Object* value = receiver->FastPropertyAt(offset);
2639 return value->IsTheHole() ? Heap::undefined_value() : value;
2640 }
2641 } else {
2642 // Attempt dictionary lookup.
2643 Dictionary* dictionary = receiver->property_dictionary();
2644 int entry = dictionary->FindStringEntry(key);
2645 if ((entry != DescriptorArray::kNotFound) &&
2646 (dictionary->DetailsAt(entry).type() == NORMAL)) {
2647 return dictionary->ValueAt(entry);
2648 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002649 }
2650 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651
2652 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002653 return Runtime::GetObjectProperty(args.at<Object>(0),
2654 args.at<Object>(1));
2655}
2656
2657
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002658Object* Runtime::SetObjectProperty(Handle<Object> object,
2659 Handle<Object> key,
2660 Handle<Object> value,
2661 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002662 HandleScope scope;
2663
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002664 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002665 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002666 Handle<Object> error =
2667 Factory::NewTypeError("non_object_property_store",
2668 HandleVector(args, 2));
2669 return Top::Throw(*error);
2670 }
2671
2672 // If the object isn't a JavaScript object, we ignore the store.
2673 if (!object->IsJSObject()) return *value;
2674
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002675 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2676
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002677 // Check if the given key is an array index.
2678 uint32_t index;
2679 if (Array::IndexFromObject(*key, &index)) {
2680 ASSERT(attr == NONE);
2681
2682 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2683 // of a string using [] notation. We need to support this too in
2684 // JavaScript.
2685 // In the case of a String object we just need to redirect the assignment to
2686 // the underlying string if the index is in range. Since the underlying
2687 // string does nothing with the assignment then we can ignore such
2688 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002689 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002690 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002691 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002692
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002693 Handle<Object> result = SetElement(js_object, index, value);
2694 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002695 return *value;
2696 }
2697
2698 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002699 Handle<Object> result;
2700 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002701 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002702 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002703 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002704 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002705 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002706 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002707 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002708 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002709 return *value;
2710 }
2711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002712 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002713 bool has_pending_exception = false;
2714 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2715 if (has_pending_exception) return Failure::Exception();
2716 Handle<String> name = Handle<String>::cast(converted);
2717
2718 if (name->AsArrayIndex(&index)) {
2719 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002720 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002721 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002722 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002723 }
2724}
2725
2726
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002727Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2728 Handle<Object> key,
2729 Handle<Object> value,
2730 PropertyAttributes attr) {
2731 HandleScope scope;
2732
2733 // Check if the given key is an array index.
2734 uint32_t index;
2735 if (Array::IndexFromObject(*key, &index)) {
2736 ASSERT(attr == NONE);
2737
2738 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2739 // of a string using [] notation. We need to support this too in
2740 // JavaScript.
2741 // In the case of a String object we just need to redirect the assignment to
2742 // the underlying string if the index is in range. Since the underlying
2743 // string does nothing with the assignment then we can ignore such
2744 // assignments.
2745 if (js_object->IsStringObjectWithCharacterAt(index)) {
2746 return *value;
2747 }
2748
2749 return js_object->SetElement(index, *value);
2750 }
2751
2752 if (key->IsString()) {
2753 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2754 ASSERT(attr == NONE);
2755 return js_object->SetElement(index, *value);
2756 } else {
2757 Handle<String> key_string = Handle<String>::cast(key);
2758 key_string->TryFlattenIfNotFlat();
2759 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2760 *value,
2761 attr);
2762 }
2763 }
2764
2765 // Call-back into JavaScript to convert the key to a string.
2766 bool has_pending_exception = false;
2767 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2768 if (has_pending_exception) return Failure::Exception();
2769 Handle<String> name = Handle<String>::cast(converted);
2770
2771 if (name->AsArrayIndex(&index)) {
2772 ASSERT(attr == NONE);
2773 return js_object->SetElement(index, *value);
2774 } else {
2775 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2776 }
2777}
2778
2779
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780static Object* Runtime_SetProperty(Arguments args) {
2781 NoHandleAllocation ha;
2782 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2783
2784 Handle<Object> object = args.at<Object>(0);
2785 Handle<Object> key = args.at<Object>(1);
2786 Handle<Object> value = args.at<Object>(2);
2787
2788 // Compute attributes.
2789 PropertyAttributes attributes = NONE;
2790 if (args.length() == 4) {
2791 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002792 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002793 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002794 RUNTIME_ASSERT(
2795 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2796 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002797 }
2798 return Runtime::SetObjectProperty(object, key, value, attributes);
2799}
2800
2801
2802// Set a local property, even if it is READ_ONLY. If the property does not
2803// exist, it will be added with attributes NONE.
2804static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2805 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002806 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002807 CONVERT_CHECKED(JSObject, object, args[0]);
2808 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002809 // Compute attributes.
2810 PropertyAttributes attributes = NONE;
2811 if (args.length() == 4) {
2812 CONVERT_CHECKED(Smi, value_obj, args[3]);
2813 int unchecked_value = value_obj->value();
2814 // Only attribute bits should be set.
2815 RUNTIME_ASSERT(
2816 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2817 attributes = static_cast<PropertyAttributes>(unchecked_value);
2818 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002819
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002820 return object->
2821 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002822}
2823
2824
2825static Object* Runtime_DeleteProperty(Arguments args) {
2826 NoHandleAllocation ha;
2827 ASSERT(args.length() == 2);
2828
2829 CONVERT_CHECKED(JSObject, object, args[0]);
2830 CONVERT_CHECKED(String, key, args[1]);
2831 return object->DeleteProperty(key);
2832}
2833
2834
ager@chromium.org9085a012009-05-11 19:22:57 +00002835static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2836 Handle<String> key) {
2837 if (object->HasLocalProperty(*key)) return Heap::true_value();
2838 // Handle hidden prototypes. If there's a hidden prototype above this thing
2839 // then we have to check it for properties, because they are supposed to
2840 // look like they are on this object.
2841 Handle<Object> proto(object->GetPrototype());
2842 if (proto->IsJSObject() &&
2843 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2844 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2845 }
2846 return Heap::false_value();
2847}
2848
2849
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002850static Object* Runtime_HasLocalProperty(Arguments args) {
2851 NoHandleAllocation ha;
2852 ASSERT(args.length() == 2);
2853 CONVERT_CHECKED(String, key, args[1]);
2854
ager@chromium.org9085a012009-05-11 19:22:57 +00002855 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002856 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002857 if (obj->IsJSObject()) {
2858 JSObject* object = JSObject::cast(obj);
2859 // Fast case - no interceptors.
2860 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2861 // Slow case. Either it's not there or we have an interceptor. We should
2862 // have handles for this kind of deal.
2863 HandleScope scope;
2864 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2865 Handle<String>(key));
2866 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002867 // Well, there is one exception: Handle [] on strings.
2868 uint32_t index;
2869 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002870 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002871 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002872 return Heap::true_value();
2873 }
2874 }
2875 return Heap::false_value();
2876}
2877
2878
2879static Object* Runtime_HasProperty(Arguments args) {
2880 NoHandleAllocation na;
2881 ASSERT(args.length() == 2);
2882
2883 // Only JS objects can have properties.
2884 if (args[0]->IsJSObject()) {
2885 JSObject* object = JSObject::cast(args[0]);
2886 CONVERT_CHECKED(String, key, args[1]);
2887 if (object->HasProperty(key)) return Heap::true_value();
2888 }
2889 return Heap::false_value();
2890}
2891
2892
2893static Object* Runtime_HasElement(Arguments args) {
2894 NoHandleAllocation na;
2895 ASSERT(args.length() == 2);
2896
2897 // Only JS objects can have elements.
2898 if (args[0]->IsJSObject()) {
2899 JSObject* object = JSObject::cast(args[0]);
2900 CONVERT_CHECKED(Smi, index_obj, args[1]);
2901 uint32_t index = index_obj->value();
2902 if (object->HasElement(index)) return Heap::true_value();
2903 }
2904 return Heap::false_value();
2905}
2906
2907
2908static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2909 NoHandleAllocation ha;
2910 ASSERT(args.length() == 2);
2911
2912 CONVERT_CHECKED(JSObject, object, args[0]);
2913 CONVERT_CHECKED(String, key, args[1]);
2914
2915 uint32_t index;
2916 if (key->AsArrayIndex(&index)) {
2917 return Heap::ToBoolean(object->HasElement(index));
2918 }
2919
ager@chromium.org870a0b62008-11-04 11:43:05 +00002920 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2921 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002922}
2923
2924
2925static Object* Runtime_GetPropertyNames(Arguments args) {
2926 HandleScope scope;
2927 ASSERT(args.length() == 1);
2928
2929 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2930 Handle<JSObject> object(raw_object);
2931 return *GetKeysFor(object);
2932}
2933
2934
2935// Returns either a FixedArray as Runtime_GetPropertyNames,
2936// or, if the given object has an enum cache that contains
2937// all enumerable properties of the object and its prototypes
2938// have none, the map of the object. This is used to speed up
2939// the check for deletions during a for-in.
2940static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2941 ASSERT(args.length() == 1);
2942
2943 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2944
2945 if (raw_object->IsSimpleEnum()) return raw_object->map();
2946
2947 HandleScope scope;
2948 Handle<JSObject> object(raw_object);
2949 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2950
2951 // Test again, since cache may have been built by preceding call.
2952 if (object->IsSimpleEnum()) return object->map();
2953
2954 return *content;
2955}
2956
2957
2958static Object* Runtime_GetArgumentsProperty(Arguments args) {
2959 NoHandleAllocation ha;
2960 ASSERT(args.length() == 1);
2961
2962 // Compute the frame holding the arguments.
2963 JavaScriptFrameIterator it;
2964 it.AdvanceToArgumentsFrame();
2965 JavaScriptFrame* frame = it.frame();
2966
2967 // Get the actual number of provided arguments.
2968 const uint32_t n = frame->GetProvidedParametersCount();
2969
2970 // Try to convert the key to an index. If successful and within
2971 // index return the the argument from the frame.
2972 uint32_t index;
2973 if (Array::IndexFromObject(args[0], &index) && index < n) {
2974 return frame->GetParameter(index);
2975 }
2976
2977 // Convert the key to a string.
2978 HandleScope scope;
2979 bool exception = false;
2980 Handle<Object> converted =
2981 Execution::ToString(args.at<Object>(0), &exception);
2982 if (exception) return Failure::Exception();
2983 Handle<String> key = Handle<String>::cast(converted);
2984
2985 // Try to convert the string key into an array index.
2986 if (key->AsArrayIndex(&index)) {
2987 if (index < n) {
2988 return frame->GetParameter(index);
2989 } else {
2990 return Top::initial_object_prototype()->GetElement(index);
2991 }
2992 }
2993
2994 // Handle special arguments properties.
2995 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2996 if (key->Equals(Heap::callee_symbol())) return frame->function();
2997
2998 // Lookup in the initial Object.prototype object.
2999 return Top::initial_object_prototype()->GetProperty(*key);
3000}
3001
3002
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003003static Object* Runtime_ToFastProperties(Arguments args) {
3004 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003005 Handle<Object> object = args.at<Object>(0);
3006 if (object->IsJSObject()) {
3007 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3008 js_object->TransformToFastProperties(0);
3009 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003010 return *object;
3011}
3012
3013
3014static Object* Runtime_ToSlowProperties(Arguments args) {
3015 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003016 Handle<Object> object = args.at<Object>(0);
3017 if (object->IsJSObject()) {
3018 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3019 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
3020 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003021 return *object;
3022}
3023
3024
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003025static Object* Runtime_ToBool(Arguments args) {
3026 NoHandleAllocation ha;
3027 ASSERT(args.length() == 1);
3028
3029 return args[0]->ToBoolean();
3030}
3031
3032
3033// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3034// Possible optimizations: put the type string into the oddballs.
3035static Object* Runtime_Typeof(Arguments args) {
3036 NoHandleAllocation ha;
3037
3038 Object* obj = args[0];
3039 if (obj->IsNumber()) return Heap::number_symbol();
3040 HeapObject* heap_obj = HeapObject::cast(obj);
3041
3042 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003043 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003044
3045 InstanceType instance_type = heap_obj->map()->instance_type();
3046 if (instance_type < FIRST_NONSTRING_TYPE) {
3047 return Heap::string_symbol();
3048 }
3049
3050 switch (instance_type) {
3051 case ODDBALL_TYPE:
3052 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3053 return Heap::boolean_symbol();
3054 }
3055 if (heap_obj->IsNull()) {
3056 return Heap::object_symbol();
3057 }
3058 ASSERT(heap_obj->IsUndefined());
3059 return Heap::undefined_symbol();
3060 case JS_FUNCTION_TYPE:
3061 return Heap::function_symbol();
3062 default:
3063 // For any kind of object not handled above, the spec rule for
3064 // host objects gives that it is okay to return "object"
3065 return Heap::object_symbol();
3066 }
3067}
3068
3069
3070static Object* Runtime_StringToNumber(Arguments args) {
3071 NoHandleAllocation ha;
3072 ASSERT(args.length() == 1);
3073 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003074 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003075 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3076}
3077
3078
3079static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3080 NoHandleAllocation ha;
3081 ASSERT(args.length() == 1);
3082
3083 CONVERT_CHECKED(JSArray, codes, args[0]);
3084 int length = Smi::cast(codes->length())->value();
3085
3086 // Check if the string can be ASCII.
3087 int i;
3088 for (i = 0; i < length; i++) {
3089 Object* element = codes->GetElement(i);
3090 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3091 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3092 break;
3093 }
3094
3095 Object* object = NULL;
3096 if (i == length) { // The string is ASCII.
3097 object = Heap::AllocateRawAsciiString(length);
3098 } else { // The string is not ASCII.
3099 object = Heap::AllocateRawTwoByteString(length);
3100 }
3101
3102 if (object->IsFailure()) return object;
3103 String* result = String::cast(object);
3104 for (int i = 0; i < length; i++) {
3105 Object* element = codes->GetElement(i);
3106 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003107 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003108 }
3109 return result;
3110}
3111
3112
3113// kNotEscaped is generated by the following:
3114//
3115// #!/bin/perl
3116// for (my $i = 0; $i < 256; $i++) {
3117// print "\n" if $i % 16 == 0;
3118// my $c = chr($i);
3119// my $escaped = 1;
3120// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3121// print $escaped ? "0, " : "1, ";
3122// }
3123
3124
3125static bool IsNotEscaped(uint16_t character) {
3126 // Only for 8 bit characters, the rest are always escaped (in a different way)
3127 ASSERT(character < 256);
3128 static const char kNotEscaped[256] = {
3129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3132 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3133 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3134 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3135 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3136 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3138 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3145 };
3146 return kNotEscaped[character] != 0;
3147}
3148
3149
3150static Object* Runtime_URIEscape(Arguments args) {
3151 const char hex_chars[] = "0123456789ABCDEF";
3152 NoHandleAllocation ha;
3153 ASSERT(args.length() == 1);
3154 CONVERT_CHECKED(String, source, args[0]);
3155
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003156 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003157
3158 int escaped_length = 0;
3159 int length = source->length();
3160 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003161 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003162 buffer->Reset(source);
3163 while (buffer->has_more()) {
3164 uint16_t character = buffer->GetNext();
3165 if (character >= 256) {
3166 escaped_length += 6;
3167 } else if (IsNotEscaped(character)) {
3168 escaped_length++;
3169 } else {
3170 escaped_length += 3;
3171 }
3172 // We don't allow strings that are longer than Smi range.
3173 if (!Smi::IsValid(escaped_length)) {
3174 Top::context()->mark_out_of_memory();
3175 return Failure::OutOfMemoryException();
3176 }
3177 }
3178 }
3179 // No length change implies no change. Return original string if no change.
3180 if (escaped_length == length) {
3181 return source;
3182 }
3183 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3184 if (o->IsFailure()) return o;
3185 String* destination = String::cast(o);
3186 int dest_position = 0;
3187
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003188 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003189 buffer->Rewind();
3190 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003191 uint16_t chr = buffer->GetNext();
3192 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003193 destination->Set(dest_position, '%');
3194 destination->Set(dest_position+1, 'u');
3195 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3196 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3197 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3198 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003199 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003200 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003201 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003202 dest_position++;
3203 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003204 destination->Set(dest_position, '%');
3205 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3206 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003207 dest_position += 3;
3208 }
3209 }
3210 return destination;
3211}
3212
3213
3214static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3215 static const signed char kHexValue['g'] = {
3216 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3217 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3218 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3219 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3220 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3221 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3222 -1, 10, 11, 12, 13, 14, 15 };
3223
3224 if (character1 > 'f') return -1;
3225 int hi = kHexValue[character1];
3226 if (hi == -1) return -1;
3227 if (character2 > 'f') return -1;
3228 int lo = kHexValue[character2];
3229 if (lo == -1) return -1;
3230 return (hi << 4) + lo;
3231}
3232
3233
ager@chromium.org870a0b62008-11-04 11:43:05 +00003234static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003235 int i,
3236 int length,
3237 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003238 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003239 int32_t hi = 0;
3240 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241 if (character == '%' &&
3242 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003243 source->Get(i + 1) == 'u' &&
3244 (hi = TwoDigitHex(source->Get(i + 2),
3245 source->Get(i + 3))) != -1 &&
3246 (lo = TwoDigitHex(source->Get(i + 4),
3247 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003248 *step = 6;
3249 return (hi << 8) + lo;
3250 } else if (character == '%' &&
3251 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003252 (lo = TwoDigitHex(source->Get(i + 1),
3253 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003254 *step = 3;
3255 return lo;
3256 } else {
3257 *step = 1;
3258 return character;
3259 }
3260}
3261
3262
3263static Object* Runtime_URIUnescape(Arguments args) {
3264 NoHandleAllocation ha;
3265 ASSERT(args.length() == 1);
3266 CONVERT_CHECKED(String, source, args[0]);
3267
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003268 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003269
3270 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003271 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003272
3273 int unescaped_length = 0;
3274 for (int i = 0; i < length; unescaped_length++) {
3275 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003276 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003277 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003278 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003279 i += step;
3280 }
3281
3282 // No length change implies no change. Return original string if no change.
3283 if (unescaped_length == length)
3284 return source;
3285
3286 Object* o = ascii ?
3287 Heap::AllocateRawAsciiString(unescaped_length) :
3288 Heap::AllocateRawTwoByteString(unescaped_length);
3289 if (o->IsFailure()) return o;
3290 String* destination = String::cast(o);
3291
3292 int dest_position = 0;
3293 for (int i = 0; i < length; dest_position++) {
3294 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003295 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003296 i += step;
3297 }
3298 return destination;
3299}
3300
3301
3302static Object* Runtime_StringParseInt(Arguments args) {
3303 NoHandleAllocation ha;
3304
3305 CONVERT_CHECKED(String, s, args[0]);
3306 CONVERT_DOUBLE_CHECKED(n, args[1]);
3307 int radix = FastD2I(n);
3308
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003309 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003310
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003311 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003312 int i;
3313
3314 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003315 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003316 if (i == len) return Heap::nan_value();
3317
3318 // Compute the sign (default to +).
3319 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003320 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003321 sign = -1;
3322 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003323 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003324 i++;
3325 }
3326
3327 // Compute the radix if 0.
3328 if (radix == 0) {
3329 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003330 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003331 radix = 8;
3332 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003333 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003334 if (c == 'x' || c == 'X') {
3335 radix = 16;
3336 i += 2;
3337 }
3338 }
3339 }
3340 } else if (radix == 16) {
3341 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003342 if (i + 1 < len && s->Get(i) == '0') {
3343 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003344 if (c == 'x' || c == 'X') i += 2;
3345 }
3346 }
3347
3348 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3349 double value;
3350 int end_index = StringToInt(s, i, radix, &value);
3351 if (end_index != i) {
3352 return Heap::NumberFromDouble(sign * value);
3353 }
3354 return Heap::nan_value();
3355}
3356
3357
3358static Object* Runtime_StringParseFloat(Arguments args) {
3359 NoHandleAllocation ha;
3360 CONVERT_CHECKED(String, str, args[0]);
3361
3362 // ECMA-262 section 15.1.2.3, empty string is NaN
3363 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3364
3365 // Create a number object from the value.
3366 return Heap::NumberFromDouble(value);
3367}
3368
3369
3370static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3371static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3372
3373
3374template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003375static Object* ConvertCaseHelper(String* s,
3376 int length,
3377 int input_string_length,
3378 unibrow::Mapping<Converter, 128>* mapping) {
3379 // We try this twice, once with the assumption that the result is no longer
3380 // than the input and, if that assumption breaks, again with the exact
3381 // length. This may not be pretty, but it is nicer than what was here before
3382 // and I hereby claim my vaffel-is.
3383 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003384 // Allocate the resulting string.
3385 //
3386 // NOTE: This assumes that the upper/lower case of an ascii
3387 // character is also ascii. This is currently the case, but it
3388 // might break in the future if we implement more context and locale
3389 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003390 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003391 ? Heap::AllocateRawAsciiString(length)
3392 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003393 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003394 String* result = String::cast(o);
3395 bool has_changed_character = false;
3396
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003397 // Convert all characters to upper case, assuming that they will fit
3398 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003399 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003400 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003401 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003402 // We can assume that the string is not empty
3403 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003404 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003405 bool has_next = buffer->has_more();
3406 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003407 int char_length = mapping->get(current, next, chars);
3408 if (char_length == 0) {
3409 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003410 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003411 i++;
3412 } else if (char_length == 1) {
3413 // Common case: converting the letter resulted in one character.
3414 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003415 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003416 has_changed_character = true;
3417 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003418 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003419 // We've assumed that the result would be as long as the
3420 // input but here is a character that converts to several
3421 // characters. No matter, we calculate the exact length
3422 // of the result and try the whole thing again.
3423 //
3424 // Note that this leaves room for optimization. We could just
3425 // memcpy what we already have to the result string. Also,
3426 // the result string is the last object allocated we could
3427 // "realloc" it and probably, in the vast majority of cases,
3428 // extend the existing string to be able to hold the full
3429 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003430 int next_length = 0;
3431 if (has_next) {
3432 next_length = mapping->get(next, 0, chars);
3433 if (next_length == 0) next_length = 1;
3434 }
3435 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003436 while (buffer->has_more()) {
3437 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003438 // NOTE: we use 0 as the next character here because, while
3439 // the next character may affect what a character converts to,
3440 // it does not in any case affect the length of what it convert
3441 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003442 int char_length = mapping->get(current, 0, chars);
3443 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003444 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003445 if (current_length > Smi::kMaxValue) {
3446 Top::context()->mark_out_of_memory();
3447 return Failure::OutOfMemoryException();
3448 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003449 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003450 // Try again with the real length.
3451 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003452 } else {
3453 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003454 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003455 i++;
3456 }
3457 has_changed_character = true;
3458 }
3459 current = next;
3460 }
3461 if (has_changed_character) {
3462 return result;
3463 } else {
3464 // If we didn't actually change anything in doing the conversion
3465 // we simple return the result and let the converted string
3466 // become garbage; there is no reason to keep two identical strings
3467 // alive.
3468 return s;
3469 }
3470}
3471
3472
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003473template <class Converter>
3474static Object* ConvertCase(Arguments args,
3475 unibrow::Mapping<Converter, 128>* mapping) {
3476 NoHandleAllocation ha;
3477
3478 CONVERT_CHECKED(String, s, args[0]);
3479 s->TryFlattenIfNotFlat();
3480
3481 int input_string_length = s->length();
3482 // Assume that the string is not empty; we need this assumption later
3483 if (input_string_length == 0) return s;
3484 int length = input_string_length;
3485
3486 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3487 if (answer->IsSmi()) {
3488 // Retry with correct length.
3489 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3490 }
3491 return answer; // This may be a failure.
3492}
3493
3494
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003495static Object* Runtime_StringToLowerCase(Arguments args) {
3496 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3497}
3498
3499
3500static Object* Runtime_StringToUpperCase(Arguments args) {
3501 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3502}
3503
3504
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003505bool Runtime::IsUpperCaseChar(uint16_t ch) {
3506 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3507 int char_length = to_upper_mapping.get(ch, 0, chars);
3508 return char_length == 0;
3509}
3510
3511
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003512static Object* Runtime_NumberToString(Arguments args) {
3513 NoHandleAllocation ha;
3514 ASSERT(args.length() == 1);
3515
3516 Object* number = args[0];
3517 RUNTIME_ASSERT(number->IsNumber());
3518
3519 Object* cached = Heap::GetNumberStringCache(number);
3520 if (cached != Heap::undefined_value()) {
3521 return cached;
3522 }
3523
3524 char arr[100];
3525 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3526 const char* str;
3527 if (number->IsSmi()) {
3528 int num = Smi::cast(number)->value();
3529 str = IntToCString(num, buffer);
3530 } else {
3531 double num = HeapNumber::cast(number)->value();
3532 str = DoubleToCString(num, buffer);
3533 }
3534 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3535
3536 if (!result->IsFailure()) {
3537 Heap::SetNumberStringCache(number, String::cast(result));
3538 }
3539 return result;
3540}
3541
3542
3543static Object* Runtime_NumberToInteger(Arguments args) {
3544 NoHandleAllocation ha;
3545 ASSERT(args.length() == 1);
3546
3547 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003548 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003549 CONVERT_DOUBLE_CHECKED(number, obj);
3550 return Heap::NumberFromDouble(DoubleToInteger(number));
3551}
3552
3553
3554static Object* Runtime_NumberToJSUint32(Arguments args) {
3555 NoHandleAllocation ha;
3556 ASSERT(args.length() == 1);
3557
3558 Object* obj = args[0];
3559 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3560 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3561 return Heap::NumberFromUint32(number);
3562}
3563
3564
3565static Object* Runtime_NumberToJSInt32(Arguments args) {
3566 NoHandleAllocation ha;
3567 ASSERT(args.length() == 1);
3568
3569 Object* obj = args[0];
3570 if (obj->IsSmi()) return obj;
3571 CONVERT_DOUBLE_CHECKED(number, obj);
3572 return Heap::NumberFromInt32(DoubleToInt32(number));
3573}
3574
3575
ager@chromium.org870a0b62008-11-04 11:43:05 +00003576// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3577// a small integer.
3578static Object* Runtime_NumberToSmi(Arguments args) {
3579 NoHandleAllocation ha;
3580 ASSERT(args.length() == 1);
3581
3582 Object* obj = args[0];
3583 if (obj->IsSmi()) {
3584 return obj;
3585 }
3586 if (obj->IsHeapNumber()) {
3587 double value = HeapNumber::cast(obj)->value();
3588 int int_value = FastD2I(value);
3589 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3590 return Smi::FromInt(int_value);
3591 }
3592 }
3593 return Heap::nan_value();
3594}
3595
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003596
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003597static Object* Runtime_NumberAdd(Arguments args) {
3598 NoHandleAllocation ha;
3599 ASSERT(args.length() == 2);
3600
3601 CONVERT_DOUBLE_CHECKED(x, args[0]);
3602 CONVERT_DOUBLE_CHECKED(y, args[1]);
3603 return Heap::AllocateHeapNumber(x + y);
3604}
3605
3606
3607static Object* Runtime_NumberSub(Arguments args) {
3608 NoHandleAllocation ha;
3609 ASSERT(args.length() == 2);
3610
3611 CONVERT_DOUBLE_CHECKED(x, args[0]);
3612 CONVERT_DOUBLE_CHECKED(y, args[1]);
3613 return Heap::AllocateHeapNumber(x - y);
3614}
3615
3616
3617static Object* Runtime_NumberMul(Arguments args) {
3618 NoHandleAllocation ha;
3619 ASSERT(args.length() == 2);
3620
3621 CONVERT_DOUBLE_CHECKED(x, args[0]);
3622 CONVERT_DOUBLE_CHECKED(y, args[1]);
3623 return Heap::AllocateHeapNumber(x * y);
3624}
3625
3626
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003627static Object* Runtime_NumberUnaryMinus(Arguments args) {
3628 NoHandleAllocation ha;
3629 ASSERT(args.length() == 1);
3630
3631 CONVERT_DOUBLE_CHECKED(x, args[0]);
3632 return Heap::AllocateHeapNumber(-x);
3633}
3634
3635
3636static Object* Runtime_NumberDiv(Arguments args) {
3637 NoHandleAllocation ha;
3638 ASSERT(args.length() == 2);
3639
3640 CONVERT_DOUBLE_CHECKED(x, args[0]);
3641 CONVERT_DOUBLE_CHECKED(y, args[1]);
3642 return Heap::NewNumberFromDouble(x / y);
3643}
3644
3645
3646static Object* Runtime_NumberMod(Arguments args) {
3647 NoHandleAllocation ha;
3648 ASSERT(args.length() == 2);
3649
3650 CONVERT_DOUBLE_CHECKED(x, args[0]);
3651 CONVERT_DOUBLE_CHECKED(y, args[1]);
3652
3653#ifdef WIN32
3654 // Workaround MS fmod bugs. ECMA-262 says:
3655 // dividend is finite and divisor is an infinity => result equals dividend
3656 // dividend is a zero and divisor is nonzero finite => result equals dividend
3657 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3658 !(x == 0 && (y != 0 && isfinite(y))))
3659#endif
3660 x = fmod(x, y);
3661 // NewNumberFromDouble may return a Smi instead of a Number object
3662 return Heap::NewNumberFromDouble(x);
3663}
3664
3665
3666static Object* Runtime_StringAdd(Arguments args) {
3667 NoHandleAllocation ha;
3668 ASSERT(args.length() == 2);
3669
3670 CONVERT_CHECKED(String, str1, args[0]);
3671 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003672 int len1 = str1->length();
3673 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003674 if (len1 == 0) return str2;
3675 if (len2 == 0) return str1;
3676 int length_sum = len1 + len2;
3677 // Make sure that an out of memory exception is thrown if the length
3678 // of the new cons string is too large to fit in a Smi.
3679 if (length_sum > Smi::kMaxValue || length_sum < 0) {
3680 Top::context()->mark_out_of_memory();
3681 return Failure::OutOfMemoryException();
3682 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003683 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003684}
3685
3686
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003687template<typename sinkchar>
3688static inline void StringBuilderConcatHelper(String* special,
3689 sinkchar* sink,
3690 FixedArray* fixed_array,
3691 int array_length) {
3692 int position = 0;
3693 for (int i = 0; i < array_length; i++) {
3694 Object* element = fixed_array->get(i);
3695 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003696 int encoded_slice = Smi::cast(element)->value();
3697 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3698 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003699 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003700 sink + position,
3701 pos,
3702 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003703 position += len;
3704 } else {
3705 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003706 int element_length = string->length();
3707 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003708 position += element_length;
3709 }
3710 }
3711}
3712
3713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003714static Object* Runtime_StringBuilderConcat(Arguments args) {
3715 NoHandleAllocation ha;
3716 ASSERT(args.length() == 2);
3717 CONVERT_CHECKED(JSArray, array, args[0]);
3718 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003719 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003720 Object* smi_array_length = array->length();
3721 if (!smi_array_length->IsSmi()) {
3722 Top::context()->mark_out_of_memory();
3723 return Failure::OutOfMemoryException();
3724 }
3725 int array_length = Smi::cast(smi_array_length)->value();
3726 if (!array->HasFastElements()) {
3727 return Top::Throw(Heap::illegal_argument_symbol());
3728 }
3729 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003730 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003731 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003732 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003733
3734 if (array_length == 0) {
3735 return Heap::empty_string();
3736 } else if (array_length == 1) {
3737 Object* first = fixed_array->get(0);
3738 if (first->IsString()) return first;
3739 }
3740
ager@chromium.org5ec48922009-05-05 07:25:34 +00003741 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003742 int position = 0;
3743 for (int i = 0; i < array_length; i++) {
3744 Object* elt = fixed_array->get(i);
3745 if (elt->IsSmi()) {
3746 int len = Smi::cast(elt)->value();
3747 int pos = len >> 11;
3748 len &= 0x7ff;
3749 if (pos + len > special_length) {
3750 return Top::Throw(Heap::illegal_argument_symbol());
3751 }
3752 position += len;
3753 } else if (elt->IsString()) {
3754 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003755 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003756 if (!Smi::IsValid(element_length + position)) {
3757 Top::context()->mark_out_of_memory();
3758 return Failure::OutOfMemoryException();
3759 }
3760 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003761 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003762 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003763 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003764 } else {
3765 return Top::Throw(Heap::illegal_argument_symbol());
3766 }
3767 }
3768
3769 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003770 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003771
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003772 if (ascii) {
3773 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003774 if (object->IsFailure()) return object;
3775 SeqAsciiString* answer = SeqAsciiString::cast(object);
3776 StringBuilderConcatHelper(special,
3777 answer->GetChars(),
3778 fixed_array,
3779 array_length);
3780 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003781 } else {
3782 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003783 if (object->IsFailure()) return object;
3784 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3785 StringBuilderConcatHelper(special,
3786 answer->GetChars(),
3787 fixed_array,
3788 array_length);
3789 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003791}
3792
3793
3794static Object* Runtime_NumberOr(Arguments args) {
3795 NoHandleAllocation ha;
3796 ASSERT(args.length() == 2);
3797
3798 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3799 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3800 return Heap::NumberFromInt32(x | y);
3801}
3802
3803
3804static Object* Runtime_NumberAnd(Arguments args) {
3805 NoHandleAllocation ha;
3806 ASSERT(args.length() == 2);
3807
3808 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3809 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3810 return Heap::NumberFromInt32(x & y);
3811}
3812
3813
3814static Object* Runtime_NumberXor(Arguments args) {
3815 NoHandleAllocation ha;
3816 ASSERT(args.length() == 2);
3817
3818 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3819 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3820 return Heap::NumberFromInt32(x ^ y);
3821}
3822
3823
3824static Object* Runtime_NumberNot(Arguments args) {
3825 NoHandleAllocation ha;
3826 ASSERT(args.length() == 1);
3827
3828 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3829 return Heap::NumberFromInt32(~x);
3830}
3831
3832
3833static Object* Runtime_NumberShl(Arguments args) {
3834 NoHandleAllocation ha;
3835 ASSERT(args.length() == 2);
3836
3837 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3838 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3839 return Heap::NumberFromInt32(x << (y & 0x1f));
3840}
3841
3842
3843static Object* Runtime_NumberShr(Arguments args) {
3844 NoHandleAllocation ha;
3845 ASSERT(args.length() == 2);
3846
3847 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3848 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3849 return Heap::NumberFromUint32(x >> (y & 0x1f));
3850}
3851
3852
3853static Object* Runtime_NumberSar(Arguments args) {
3854 NoHandleAllocation ha;
3855 ASSERT(args.length() == 2);
3856
3857 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3858 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3859 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3860}
3861
3862
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003863static Object* Runtime_NumberEquals(Arguments args) {
3864 NoHandleAllocation ha;
3865 ASSERT(args.length() == 2);
3866
3867 CONVERT_DOUBLE_CHECKED(x, args[0]);
3868 CONVERT_DOUBLE_CHECKED(y, args[1]);
3869 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3870 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3871 if (x == y) return Smi::FromInt(EQUAL);
3872 Object* result;
3873 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3874 result = Smi::FromInt(EQUAL);
3875 } else {
3876 result = Smi::FromInt(NOT_EQUAL);
3877 }
3878 return result;
3879}
3880
3881
3882static Object* Runtime_StringEquals(Arguments args) {
3883 NoHandleAllocation ha;
3884 ASSERT(args.length() == 2);
3885
3886 CONVERT_CHECKED(String, x, args[0]);
3887 CONVERT_CHECKED(String, y, args[1]);
3888
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003889 bool not_equal = !x->Equals(y);
3890 // This is slightly convoluted because the value that signifies
3891 // equality is 0 and inequality is 1 so we have to negate the result
3892 // from String::Equals.
3893 ASSERT(not_equal == 0 || not_equal == 1);
3894 STATIC_CHECK(EQUAL == 0);
3895 STATIC_CHECK(NOT_EQUAL == 1);
3896 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003897}
3898
3899
3900static Object* Runtime_NumberCompare(Arguments args) {
3901 NoHandleAllocation ha;
3902 ASSERT(args.length() == 3);
3903
3904 CONVERT_DOUBLE_CHECKED(x, args[0]);
3905 CONVERT_DOUBLE_CHECKED(y, args[1]);
3906 if (isnan(x) || isnan(y)) return args[2];
3907 if (x == y) return Smi::FromInt(EQUAL);
3908 if (isless(x, y)) return Smi::FromInt(LESS);
3909 return Smi::FromInt(GREATER);
3910}
3911
3912
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003913// Compare two Smis as if they were converted to strings and then
3914// compared lexicographically.
3915static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3916 NoHandleAllocation ha;
3917 ASSERT(args.length() == 2);
3918
3919 // Arrays for the individual characters of the two Smis. Smis are
3920 // 31 bit integers and 10 decimal digits are therefore enough.
3921 static int x_elms[10];
3922 static int y_elms[10];
3923
3924 // Extract the integer values from the Smis.
3925 CONVERT_CHECKED(Smi, x, args[0]);
3926 CONVERT_CHECKED(Smi, y, args[1]);
3927 int x_value = x->value();
3928 int y_value = y->value();
3929
3930 // If the integers are equal so are the string representations.
3931 if (x_value == y_value) return Smi::FromInt(EQUAL);
3932
3933 // If one of the integers are zero the normal integer order is the
3934 // same as the lexicographic order of the string representations.
3935 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3936
ager@chromium.org32912102009-01-16 10:38:43 +00003937 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003938 // smallest because the char code of '-' is less than the char code
3939 // of any digit. Otherwise, we make both values positive.
3940 if (x_value < 0 || y_value < 0) {
3941 if (y_value >= 0) return Smi::FromInt(LESS);
3942 if (x_value >= 0) return Smi::FromInt(GREATER);
3943 x_value = -x_value;
3944 y_value = -y_value;
3945 }
3946
3947 // Convert the integers to arrays of their decimal digits.
3948 int x_index = 0;
3949 int y_index = 0;
3950 while (x_value > 0) {
3951 x_elms[x_index++] = x_value % 10;
3952 x_value /= 10;
3953 }
3954 while (y_value > 0) {
3955 y_elms[y_index++] = y_value % 10;
3956 y_value /= 10;
3957 }
3958
3959 // Loop through the arrays of decimal digits finding the first place
3960 // where they differ.
3961 while (--x_index >= 0 && --y_index >= 0) {
3962 int diff = x_elms[x_index] - y_elms[y_index];
3963 if (diff != 0) return Smi::FromInt(diff);
3964 }
3965
3966 // If one array is a suffix of the other array, the longest array is
3967 // the representation of the largest of the Smis in the
3968 // lexicographic ordering.
3969 return Smi::FromInt(x_index - y_index);
3970}
3971
3972
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003973static Object* Runtime_StringCompare(Arguments args) {
3974 NoHandleAllocation ha;
3975 ASSERT(args.length() == 2);
3976
3977 CONVERT_CHECKED(String, x, args[0]);
3978 CONVERT_CHECKED(String, y, args[1]);
3979
3980 // A few fast case tests before we flatten.
3981 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003982 if (y->length() == 0) {
3983 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003984 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003985 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003986 return Smi::FromInt(LESS);
3987 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003988
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003989 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003990 if (d < 0) return Smi::FromInt(LESS);
3991 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003992
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003993 x->TryFlattenIfNotFlat();
3994 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003995
3996 static StringInputBuffer bufx;
3997 static StringInputBuffer bufy;
3998 bufx.Reset(x);
3999 bufy.Reset(y);
4000 while (bufx.has_more() && bufy.has_more()) {
4001 int d = bufx.GetNext() - bufy.GetNext();
4002 if (d < 0) return Smi::FromInt(LESS);
4003 else if (d > 0) return Smi::FromInt(GREATER);
4004 }
4005
4006 // x is (non-trivial) prefix of y:
4007 if (bufy.has_more()) return Smi::FromInt(LESS);
4008 // y is prefix of x:
4009 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4010}
4011
4012
4013static Object* Runtime_Math_abs(Arguments args) {
4014 NoHandleAllocation ha;
4015 ASSERT(args.length() == 1);
4016
4017 CONVERT_DOUBLE_CHECKED(x, args[0]);
4018 return Heap::AllocateHeapNumber(fabs(x));
4019}
4020
4021
4022static Object* Runtime_Math_acos(Arguments args) {
4023 NoHandleAllocation ha;
4024 ASSERT(args.length() == 1);
4025
4026 CONVERT_DOUBLE_CHECKED(x, args[0]);
4027 return Heap::AllocateHeapNumber(acos(x));
4028}
4029
4030
4031static Object* Runtime_Math_asin(Arguments args) {
4032 NoHandleAllocation ha;
4033 ASSERT(args.length() == 1);
4034
4035 CONVERT_DOUBLE_CHECKED(x, args[0]);
4036 return Heap::AllocateHeapNumber(asin(x));
4037}
4038
4039
4040static Object* Runtime_Math_atan(Arguments args) {
4041 NoHandleAllocation ha;
4042 ASSERT(args.length() == 1);
4043
4044 CONVERT_DOUBLE_CHECKED(x, args[0]);
4045 return Heap::AllocateHeapNumber(atan(x));
4046}
4047
4048
4049static Object* Runtime_Math_atan2(Arguments args) {
4050 NoHandleAllocation ha;
4051 ASSERT(args.length() == 2);
4052
4053 CONVERT_DOUBLE_CHECKED(x, args[0]);
4054 CONVERT_DOUBLE_CHECKED(y, args[1]);
4055 double result;
4056 if (isinf(x) && isinf(y)) {
4057 // Make sure that the result in case of two infinite arguments
4058 // is a multiple of Pi / 4. The sign of the result is determined
4059 // by the first argument (x) and the sign of the second argument
4060 // determines the multiplier: one or three.
4061 static double kPiDividedBy4 = 0.78539816339744830962;
4062 int multiplier = (x < 0) ? -1 : 1;
4063 if (y < 0) multiplier *= 3;
4064 result = multiplier * kPiDividedBy4;
4065 } else {
4066 result = atan2(x, y);
4067 }
4068 return Heap::AllocateHeapNumber(result);
4069}
4070
4071
4072static Object* Runtime_Math_ceil(Arguments args) {
4073 NoHandleAllocation ha;
4074 ASSERT(args.length() == 1);
4075
4076 CONVERT_DOUBLE_CHECKED(x, args[0]);
4077 return Heap::NumberFromDouble(ceiling(x));
4078}
4079
4080
4081static Object* Runtime_Math_cos(Arguments args) {
4082 NoHandleAllocation ha;
4083 ASSERT(args.length() == 1);
4084
4085 CONVERT_DOUBLE_CHECKED(x, args[0]);
4086 return Heap::AllocateHeapNumber(cos(x));
4087}
4088
4089
4090static Object* Runtime_Math_exp(Arguments args) {
4091 NoHandleAllocation ha;
4092 ASSERT(args.length() == 1);
4093
4094 CONVERT_DOUBLE_CHECKED(x, args[0]);
4095 return Heap::AllocateHeapNumber(exp(x));
4096}
4097
4098
4099static Object* Runtime_Math_floor(Arguments args) {
4100 NoHandleAllocation ha;
4101 ASSERT(args.length() == 1);
4102
4103 CONVERT_DOUBLE_CHECKED(x, args[0]);
4104 return Heap::NumberFromDouble(floor(x));
4105}
4106
4107
4108static Object* Runtime_Math_log(Arguments args) {
4109 NoHandleAllocation ha;
4110 ASSERT(args.length() == 1);
4111
4112 CONVERT_DOUBLE_CHECKED(x, args[0]);
4113 return Heap::AllocateHeapNumber(log(x));
4114}
4115
4116
4117static Object* Runtime_Math_pow(Arguments args) {
4118 NoHandleAllocation ha;
4119 ASSERT(args.length() == 2);
4120
4121 CONVERT_DOUBLE_CHECKED(x, args[0]);
4122 CONVERT_DOUBLE_CHECKED(y, args[1]);
4123 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4124 return Heap::nan_value();
4125 } else if (y == 0) {
4126 return Smi::FromInt(1);
4127 } else {
4128 return Heap::AllocateHeapNumber(pow(x, y));
4129 }
4130}
4131
4132// Returns a number value with positive sign, greater than or equal to
4133// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00004134static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004135 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004136 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137
4138 // To get much better precision, we combine the results of two
4139 // invocations of random(). The result is computed by normalizing a
4140 // double in the range [0, RAND_MAX + 1) obtained by adding the
4141 // high-order bits in the range [0, RAND_MAX] with the low-order
4142 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004143 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004144 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004145 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004146 ASSERT(result >= 0 && result < 1);
4147 return Heap::AllocateHeapNumber(result);
4148}
4149
4150
4151static Object* Runtime_Math_round(Arguments args) {
4152 NoHandleAllocation ha;
4153 ASSERT(args.length() == 1);
4154
4155 CONVERT_DOUBLE_CHECKED(x, args[0]);
4156 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4157 return Heap::NumberFromDouble(floor(x + 0.5));
4158}
4159
4160
4161static Object* Runtime_Math_sin(Arguments args) {
4162 NoHandleAllocation ha;
4163 ASSERT(args.length() == 1);
4164
4165 CONVERT_DOUBLE_CHECKED(x, args[0]);
4166 return Heap::AllocateHeapNumber(sin(x));
4167}
4168
4169
4170static Object* Runtime_Math_sqrt(Arguments args) {
4171 NoHandleAllocation ha;
4172 ASSERT(args.length() == 1);
4173
4174 CONVERT_DOUBLE_CHECKED(x, args[0]);
4175 return Heap::AllocateHeapNumber(sqrt(x));
4176}
4177
4178
4179static Object* Runtime_Math_tan(Arguments args) {
4180 NoHandleAllocation ha;
4181 ASSERT(args.length() == 1);
4182
4183 CONVERT_DOUBLE_CHECKED(x, args[0]);
4184 return Heap::AllocateHeapNumber(tan(x));
4185}
4186
4187
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004188// The NewArguments function is only used when constructing the
4189// arguments array when calling non-functions from JavaScript in
4190// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004191static Object* Runtime_NewArguments(Arguments args) {
4192 NoHandleAllocation ha;
4193 ASSERT(args.length() == 1);
4194
4195 // ECMA-262, 3rd., 10.1.8, p.39
4196 CONVERT_CHECKED(JSFunction, callee, args[0]);
4197
4198 // Compute the frame holding the arguments.
4199 JavaScriptFrameIterator it;
4200 it.AdvanceToArgumentsFrame();
4201 JavaScriptFrame* frame = it.frame();
4202
4203 const int length = frame->GetProvidedParametersCount();
4204 Object* result = Heap::AllocateArgumentsObject(callee, length);
4205 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004206 if (length > 0) {
4207 Object* obj = Heap::AllocateFixedArray(length);
4208 if (obj->IsFailure()) return obj;
4209 FixedArray* array = FixedArray::cast(obj);
4210 ASSERT(array->length() == length);
4211 WriteBarrierMode mode = array->GetWriteBarrierMode();
4212 for (int i = 0; i < length; i++) {
4213 array->set(i, frame->GetParameter(i), mode);
4214 }
4215 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004216 }
4217 return result;
4218}
4219
4220
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004221static Object* Runtime_NewArgumentsFast(Arguments args) {
4222 NoHandleAllocation ha;
4223 ASSERT(args.length() == 3);
4224
4225 JSFunction* callee = JSFunction::cast(args[0]);
4226 Object** parameters = reinterpret_cast<Object**>(args[1]);
4227 const int length = Smi::cast(args[2])->value();
4228
4229 Object* result = Heap::AllocateArgumentsObject(callee, length);
4230 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004231 ASSERT(Heap::InNewSpace(result));
4232
4233 // Allocate the elements if needed.
4234 if (length > 0) {
4235 // Allocate the fixed array.
4236 Object* obj = Heap::AllocateRawFixedArray(length);
4237 if (obj->IsFailure()) return obj;
4238 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4239 FixedArray* array = FixedArray::cast(obj);
4240 array->set_length(length);
4241 WriteBarrierMode mode = array->GetWriteBarrierMode();
4242 for (int i = 0; i < length; i++) {
4243 array->set(i, *--parameters, mode);
4244 }
4245 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4246 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004247 }
4248 return result;
4249}
4250
4251
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004252static Object* Runtime_NewClosure(Arguments args) {
4253 HandleScope scope;
4254 ASSERT(args.length() == 2);
4255 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4256 CONVERT_ARG_CHECKED(Context, context, 1);
4257
4258 Handle<JSFunction> result =
4259 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4260 return *result;
4261}
4262
4263
4264static Object* Runtime_NewObject(Arguments args) {
4265 NoHandleAllocation ha;
4266 ASSERT(args.length() == 1);
4267
4268 Object* constructor = args[0];
4269 if (constructor->IsJSFunction()) {
4270 JSFunction* function = JSFunction::cast(constructor);
4271
ager@chromium.org32912102009-01-16 10:38:43 +00004272 // Handle stepping into constructors if step into is active.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004273#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004274 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004275 HandleScope scope;
4276 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004277 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004278#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004279
4280 if (function->has_initial_map() &&
4281 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
4282 // The 'Function' function ignores the receiver object when
4283 // called using 'new' and creates a new JSFunction object that
4284 // is returned. The receiver object is only used for error
4285 // reporting if an error occurs when constructing the new
4286 // JSFunction. AllocateJSObject should not be used to allocate
4287 // JSFunctions since it does not properly initialize the shared
4288 // part of the function. Since the receiver is ignored anyway,
4289 // we use the global object as the receiver instead of a new
4290 // JSFunction object. This way, errors are reported the same
4291 // way whether or not 'Function' is called using 'new'.
4292 return Top::context()->global();
4293 }
4294 return Heap::AllocateJSObject(function);
4295 }
4296
4297 HandleScope scope;
4298 Handle<Object> cons(constructor);
4299 // The constructor is not a function; throw a type error.
4300 Handle<Object> type_error =
4301 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
4302 return Top::Throw(*type_error);
4303}
4304
4305
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004306static Object* Runtime_LazyCompile(Arguments args) {
4307 HandleScope scope;
4308 ASSERT(args.length() == 1);
4309
4310 Handle<JSFunction> function = args.at<JSFunction>(0);
4311#ifdef DEBUG
4312 if (FLAG_trace_lazy) {
4313 PrintF("[lazy: ");
4314 function->shared()->name()->Print();
4315 PrintF("]\n");
4316 }
4317#endif
4318
4319 // Compile the target function.
4320 ASSERT(!function->is_compiled());
4321 if (!CompileLazy(function, KEEP_EXCEPTION)) {
4322 return Failure::Exception();
4323 }
4324
4325 return function->code();
4326}
4327
4328
4329static Object* Runtime_GetCalledFunction(Arguments args) {
4330 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004331 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004332 StackFrameIterator it;
4333 // Get past the JS-to-C exit frame.
4334 ASSERT(it.frame()->is_exit());
4335 it.Advance();
4336 // Get past the CALL_NON_FUNCTION activation frame.
4337 ASSERT(it.frame()->is_java_script());
4338 it.Advance();
4339 // Argument adaptor frames do not copy the function; we have to skip
4340 // past them to get to the real calling frame.
4341 if (it.frame()->is_arguments_adaptor()) it.Advance();
4342 // Get the function from the top of the expression stack of the
4343 // calling frame.
4344 StandardFrame* frame = StandardFrame::cast(it.frame());
4345 int index = frame->ComputeExpressionsCount() - 1;
4346 Object* result = frame->GetExpression(index);
4347 return result;
4348}
4349
4350
4351static Object* Runtime_GetFunctionDelegate(Arguments args) {
4352 HandleScope scope;
4353 ASSERT(args.length() == 1);
4354 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4355 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4356}
4357
4358
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004359static Object* Runtime_GetConstructorDelegate(Arguments args) {
4360 HandleScope scope;
4361 ASSERT(args.length() == 1);
4362 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4363 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4364}
4365
4366
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004367static Object* Runtime_NewContext(Arguments args) {
4368 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004369 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004370
kasper.lund7276f142008-07-30 08:49:36 +00004371 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004372 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4373 Object* result = Heap::AllocateFunctionContext(length, function);
4374 if (result->IsFailure()) return result;
4375
4376 Top::set_context(Context::cast(result));
4377
kasper.lund7276f142008-07-30 08:49:36 +00004378 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004379}
4380
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004381static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004382 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004383 Object* js_object = object;
4384 if (!js_object->IsJSObject()) {
4385 js_object = js_object->ToObject();
4386 if (js_object->IsFailure()) {
4387 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004388 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004389 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004390 Handle<Object> result =
4391 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4392 return Top::Throw(*result);
4393 }
4394 }
4395
4396 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004397 Heap::AllocateWithContext(Top::context(),
4398 JSObject::cast(js_object),
4399 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004400 if (result->IsFailure()) return result;
4401
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004402 Context* context = Context::cast(result);
4403 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004404
kasper.lund7276f142008-07-30 08:49:36 +00004405 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004406}
4407
4408
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004409static Object* Runtime_PushContext(Arguments args) {
4410 NoHandleAllocation ha;
4411 ASSERT(args.length() == 1);
4412 return PushContextHelper(args[0], false);
4413}
4414
4415
4416static Object* Runtime_PushCatchContext(Arguments args) {
4417 NoHandleAllocation ha;
4418 ASSERT(args.length() == 1);
4419 return PushContextHelper(args[0], true);
4420}
4421
4422
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004423static Object* Runtime_LookupContext(Arguments args) {
4424 HandleScope scope;
4425 ASSERT(args.length() == 2);
4426
4427 CONVERT_ARG_CHECKED(Context, context, 0);
4428 CONVERT_ARG_CHECKED(String, name, 1);
4429
4430 int index;
4431 PropertyAttributes attributes;
4432 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004433 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004434 context->Lookup(name, flags, &index, &attributes);
4435
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004436 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004437 ASSERT(holder->IsJSObject());
4438 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439 }
4440
4441 // No intermediate context found. Use global object by default.
4442 return Top::context()->global();
4443}
4444
4445
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004446// A mechanism to return pairs of Object*'s. This is somewhat
4447// compiler-dependent as it assumes that a 64-bit value (a long long)
4448// is returned via two registers (edx:eax on ia32). Both the ia32 and
4449// arm platform support this; it is mostly an issue of "coaxing" the
4450// compiler to do the right thing.
4451//
4452// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004453// TODO(x64): Definitely!
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004454typedef uint64_t ObjectPair;
4455static inline ObjectPair MakePair(Object* x, Object* y) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004456#if V8_HOST_ARCH_64_BIT
4457 UNIMPLEMENTED();
4458 return 0;
4459#else
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004460 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004461 (reinterpret_cast<ObjectPair>(y) << 32);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004462#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004463}
4464
4465
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004466static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004467 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4468 USE(attributes);
4469 return x->IsTheHole() ? Heap::undefined_value() : x;
4470}
4471
4472
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004473static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4474 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004475 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004476 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004477 JSFunction* context_extension_function =
4478 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004479 // If the holder isn't a context extension object, we just return it
4480 // as the receiver. This allows arguments objects to be used as
4481 // receivers, but only if they are put in the context scope chain
4482 // explicitly via a with-statement.
4483 Object* constructor = holder->map()->constructor();
4484 if (constructor != context_extension_function) return holder;
4485 // Fall back to using the global object as the receiver if the
4486 // property turns out to be a local variable allocated in a context
4487 // extension object - introduced via eval.
4488 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004489}
4490
4491
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004492static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004493 HandleScope scope;
4494 ASSERT(args.length() == 2);
4495
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004496 if (!args[0]->IsContext() || !args[1]->IsString()) {
4497 return MakePair(IllegalOperation(), NULL);
4498 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004499 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004500 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004501
4502 int index;
4503 PropertyAttributes attributes;
4504 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004505 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004506 context->Lookup(name, flags, &index, &attributes);
4507
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004508 // If the index is non-negative, the slot has been found in a local
4509 // variable or a parameter. Read it from the context object or the
4510 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004511 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004512 // If the "property" we were looking for is a local variable or an
4513 // argument in a context, the receiver is the global object; see
4514 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4515 JSObject* receiver = Top::context()->global()->global_receiver();
4516 Object* value = (holder->IsContext())
4517 ? Context::cast(*holder)->get(index)
4518 : JSObject::cast(*holder)->GetElement(index);
4519 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004520 }
4521
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004522 // If the holder is found, we read the property from it.
4523 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004524 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004525 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004526 JSObject* receiver;
4527 if (object->IsGlobalObject()) {
4528 receiver = GlobalObject::cast(object)->global_receiver();
4529 } else if (context->is_exception_holder(*holder)) {
4530 receiver = Top::context()->global()->global_receiver();
4531 } else {
4532 receiver = ComputeReceiverForNonGlobal(object);
4533 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004534 // No need to unhole the value here. This is taken care of by the
4535 // GetProperty function.
4536 Object* value = object->GetProperty(*name);
4537 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538 }
4539
4540 if (throw_error) {
4541 // The property doesn't exist - throw exception.
4542 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004543 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004544 return MakePair(Top::Throw(*reference_error), NULL);
4545 } else {
4546 // The property doesn't exist - return undefined
4547 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4548 }
4549}
4550
4551
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004552static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004553 return LoadContextSlotHelper(args, true);
4554}
4555
4556
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004557static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004558 return LoadContextSlotHelper(args, false);
4559}
4560
4561
4562static Object* Runtime_StoreContextSlot(Arguments args) {
4563 HandleScope scope;
4564 ASSERT(args.length() == 3);
4565
4566 Handle<Object> value(args[0]);
4567 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004568 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004569
4570 int index;
4571 PropertyAttributes attributes;
4572 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004573 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004574 context->Lookup(name, flags, &index, &attributes);
4575
4576 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004577 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004578 // Ignore if read_only variable.
4579 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004580 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004581 }
4582 } else {
4583 ASSERT((attributes & READ_ONLY) == 0);
4584 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004585 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004586 USE(result);
4587 ASSERT(!result->IsFailure());
4588 }
4589 return *value;
4590 }
4591
4592 // Slow case: The property is not in a FixedArray context.
4593 // It is either in an JSObject extension context or it was not found.
4594 Handle<JSObject> context_ext;
4595
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004596 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004597 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004598 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004599 } else {
4600 // The property was not found. It needs to be stored in the global context.
4601 ASSERT(attributes == ABSENT);
4602 attributes = NONE;
4603 context_ext = Handle<JSObject>(Top::context()->global());
4604 }
4605
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004606 // Set the property, but ignore if read_only variable on the context
4607 // extension object itself.
4608 if ((attributes & READ_ONLY) == 0 ||
4609 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004610 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4611 if (set.is_null()) {
4612 // Failure::Exception is converted to a null handle in the
4613 // handle-based methods such as SetProperty. We therefore need
4614 // to convert null handles back to exceptions.
4615 ASSERT(Top::has_pending_exception());
4616 return Failure::Exception();
4617 }
4618 }
4619 return *value;
4620}
4621
4622
4623static Object* Runtime_Throw(Arguments args) {
4624 HandleScope scope;
4625 ASSERT(args.length() == 1);
4626
4627 return Top::Throw(args[0]);
4628}
4629
4630
4631static Object* Runtime_ReThrow(Arguments args) {
4632 HandleScope scope;
4633 ASSERT(args.length() == 1);
4634
4635 return Top::ReThrow(args[0]);
4636}
4637
4638
4639static Object* Runtime_ThrowReferenceError(Arguments args) {
4640 HandleScope scope;
4641 ASSERT(args.length() == 1);
4642
4643 Handle<Object> name(args[0]);
4644 Handle<Object> reference_error =
4645 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4646 return Top::Throw(*reference_error);
4647}
4648
4649
4650static Object* Runtime_StackOverflow(Arguments args) {
4651 NoHandleAllocation na;
4652 return Top::StackOverflow();
4653}
4654
4655
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004656static Object* Runtime_StackGuard(Arguments args) {
4657 ASSERT(args.length() == 1);
4658
4659 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004660 if (StackGuard::IsStackOverflow()) {
4661 return Runtime_StackOverflow(args);
4662 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004663
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004664 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004665}
4666
4667
4668// NOTE: These PrintXXX functions are defined for all builds (not just
4669// DEBUG builds) because we may want to be able to trace function
4670// calls in all modes.
4671static void PrintString(String* str) {
4672 // not uncommon to have empty strings
4673 if (str->length() > 0) {
4674 SmartPointer<char> s =
4675 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4676 PrintF("%s", *s);
4677 }
4678}
4679
4680
4681static void PrintObject(Object* obj) {
4682 if (obj->IsSmi()) {
4683 PrintF("%d", Smi::cast(obj)->value());
4684 } else if (obj->IsString() || obj->IsSymbol()) {
4685 PrintString(String::cast(obj));
4686 } else if (obj->IsNumber()) {
4687 PrintF("%g", obj->Number());
4688 } else if (obj->IsFailure()) {
4689 PrintF("<failure>");
4690 } else if (obj->IsUndefined()) {
4691 PrintF("<undefined>");
4692 } else if (obj->IsNull()) {
4693 PrintF("<null>");
4694 } else if (obj->IsTrue()) {
4695 PrintF("<true>");
4696 } else if (obj->IsFalse()) {
4697 PrintF("<false>");
4698 } else {
4699 PrintF("%p", obj);
4700 }
4701}
4702
4703
4704static int StackSize() {
4705 int n = 0;
4706 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4707 return n;
4708}
4709
4710
4711static void PrintTransition(Object* result) {
4712 // indentation
4713 { const int nmax = 80;
4714 int n = StackSize();
4715 if (n <= nmax)
4716 PrintF("%4d:%*s", n, n, "");
4717 else
4718 PrintF("%4d:%*s", n, nmax, "...");
4719 }
4720
4721 if (result == NULL) {
4722 // constructor calls
4723 JavaScriptFrameIterator it;
4724 JavaScriptFrame* frame = it.frame();
4725 if (frame->IsConstructor()) PrintF("new ");
4726 // function name
4727 Object* fun = frame->function();
4728 if (fun->IsJSFunction()) {
4729 PrintObject(JSFunction::cast(fun)->shared()->name());
4730 } else {
4731 PrintObject(fun);
4732 }
4733 // function arguments
4734 // (we are intentionally only printing the actually
4735 // supplied parameters, not all parameters required)
4736 PrintF("(this=");
4737 PrintObject(frame->receiver());
4738 const int length = frame->GetProvidedParametersCount();
4739 for (int i = 0; i < length; i++) {
4740 PrintF(", ");
4741 PrintObject(frame->GetParameter(i));
4742 }
4743 PrintF(") {\n");
4744
4745 } else {
4746 // function result
4747 PrintF("} -> ");
4748 PrintObject(result);
4749 PrintF("\n");
4750 }
4751}
4752
4753
4754static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004755 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004756 NoHandleAllocation ha;
4757 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004758 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004759}
4760
4761
4762static Object* Runtime_TraceExit(Arguments args) {
4763 NoHandleAllocation ha;
4764 PrintTransition(args[0]);
4765 return args[0]; // return TOS
4766}
4767
4768
4769static Object* Runtime_DebugPrint(Arguments args) {
4770 NoHandleAllocation ha;
4771 ASSERT(args.length() == 1);
4772
4773#ifdef DEBUG
4774 if (args[0]->IsString()) {
4775 // If we have a string, assume it's a code "marker"
4776 // and print some interesting cpu debugging info.
4777 JavaScriptFrameIterator it;
4778 JavaScriptFrame* frame = it.frame();
4779 PrintF("fp = %p, sp = %p, pp = %p: ",
4780 frame->fp(), frame->sp(), frame->pp());
4781 } else {
4782 PrintF("DebugPrint: ");
4783 }
4784 args[0]->Print();
4785#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004786 // ShortPrint is available in release mode. Print is not.
4787 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004788#endif
4789 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004790 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004791
4792 return args[0]; // return TOS
4793}
4794
4795
4796static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004797 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004798 NoHandleAllocation ha;
4799 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004800 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004801}
4802
4803
mads.s.ager31e71382008-08-13 09:32:07 +00004804static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004805 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004806 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004807
4808 // According to ECMA-262, section 15.9.1, page 117, the precision of
4809 // the number in a Date object representing a particular instant in
4810 // time is milliseconds. Therefore, we floor the result of getting
4811 // the OS time.
4812 double millis = floor(OS::TimeCurrentMillis());
4813 return Heap::NumberFromDouble(millis);
4814}
4815
4816
4817static Object* Runtime_DateParseString(Arguments args) {
4818 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004819 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004821 CONVERT_ARG_CHECKED(String, str, 0);
4822 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004823
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004824 CONVERT_ARG_CHECKED(JSArray, output, 1);
4825 RUNTIME_ASSERT(output->HasFastElements());
4826
4827 AssertNoAllocation no_allocation;
4828
4829 FixedArray* output_array = output->elements();
4830 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4831 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004832 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004833 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004834 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004835 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004836 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4837 }
4838
4839 if (result) {
4840 return *output;
4841 } else {
4842 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004843 }
4844}
4845
4846
4847static Object* Runtime_DateLocalTimezone(Arguments args) {
4848 NoHandleAllocation ha;
4849 ASSERT(args.length() == 1);
4850
4851 CONVERT_DOUBLE_CHECKED(x, args[0]);
4852 char* zone = OS::LocalTimezone(x);
4853 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4854}
4855
4856
4857static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4858 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004859 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860
4861 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4862}
4863
4864
4865static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4866 NoHandleAllocation ha;
4867 ASSERT(args.length() == 1);
4868
4869 CONVERT_DOUBLE_CHECKED(x, args[0]);
4870 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4871}
4872
4873
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004874static Object* Runtime_NumberIsFinite(Arguments args) {
4875 NoHandleAllocation ha;
4876 ASSERT(args.length() == 1);
4877
4878 CONVERT_DOUBLE_CHECKED(value, args[0]);
4879 Object* result;
4880 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4881 result = Heap::false_value();
4882 } else {
4883 result = Heap::true_value();
4884 }
4885 return result;
4886}
4887
4888
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004889static Object* Runtime_GlobalReceiver(Arguments args) {
4890 ASSERT(args.length() == 1);
4891 Object* global = args[0];
4892 if (!global->IsJSGlobalObject()) return Heap::null_value();
4893 return JSGlobalObject::cast(global)->global_receiver();
4894}
4895
4896
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004897static Object* Runtime_CompileString(Arguments args) {
4898 HandleScope scope;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004899 ASSERT_EQ(3, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004900 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00004901 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004902 CONVERT_ARG_CHECKED(Oddball, is_json, 2)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004903
ager@chromium.org381abbb2009-02-25 13:23:22 +00004904 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004905 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004906 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4907 context,
4908 line_offset->value(),
4909 true,
4910 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004911 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004912 Handle<JSFunction> fun =
4913 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4914 return *fun;
4915}
4916
4917
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004918static Handle<JSFunction> GetBuiltinFunction(String* name) {
4919 LookupResult result;
4920 Top::global_context()->builtins()->LocalLookup(name, &result);
4921 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4922}
4923
4924
4925static Object* CompileDirectEval(Handle<String> source) {
4926 // Compute the eval context.
4927 HandleScope scope;
4928 StackFrameLocator locator;
4929 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4930 Handle<Context> context(Context::cast(frame->context()));
4931 bool is_global = context->IsGlobalContext();
4932
ager@chromium.org381abbb2009-02-25 13:23:22 +00004933 // Compile source string in the current context.
4934 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004935 Compiler::CompileEval(source, context, 0, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004936 if (boilerplate.is_null()) return Failure::Exception();
4937 Handle<JSFunction> fun =
4938 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4939 return *fun;
4940}
4941
4942
4943static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4944 ASSERT(args.length() == 2);
4945
4946 HandleScope scope;
4947
4948 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4949
4950 Handle<Object> receiver;
4951
4952 // Find where the 'eval' symbol is bound. It is unaliased only if
4953 // it is bound in the global context.
4954 StackFrameLocator locator;
4955 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4956 Handle<Context> context(Context::cast(frame->context()));
4957 int index;
4958 PropertyAttributes attributes;
4959 while (!context.is_null()) {
4960 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4961 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00004962 // Stop search when eval is found or when the global context is
4963 // reached.
4964 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004965 if (context->is_function_context()) {
4966 context = Handle<Context>(Context::cast(context->closure()->context()));
4967 } else {
4968 context = Handle<Context>(context->previous());
4969 }
4970 }
4971
iposva@chromium.org245aa852009-02-10 00:49:54 +00004972 // If eval could not be resolved, it has been deleted and we need to
4973 // throw a reference error.
4974 if (attributes == ABSENT) {
4975 Handle<Object> name = Factory::eval_symbol();
4976 Handle<Object> reference_error =
4977 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4978 return Top::Throw(*reference_error);
4979 }
4980
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004981 if (context->IsGlobalContext()) {
4982 // 'eval' is bound in the global context, but it may have been overwritten.
4983 // Compare it to the builtin 'GlobalEval' function to make sure.
4984 Handle<JSFunction> global_eval =
4985 GetBuiltinFunction(Heap::global_eval_symbol());
4986 if (global_eval.is_identical_to(callee)) {
4987 // A direct eval call.
4988 if (args[1]->IsString()) {
4989 CONVERT_ARG_CHECKED(String, source, 1);
4990 // A normal eval call on a string. Compile it and return the
4991 // compiled function bound in the local context.
4992 Object* compiled_source = CompileDirectEval(source);
4993 if (compiled_source->IsFailure()) return compiled_source;
4994 receiver = Handle<Object>(frame->receiver());
4995 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4996 } else {
4997 // An eval call that is not called on a string. Global eval
4998 // deals better with this.
4999 receiver = Handle<Object>(Top::global_context()->global());
5000 }
5001 } else {
5002 // 'eval' is overwritten. Just call the function with the given arguments.
5003 receiver = Handle<Object>(Top::global_context()->global());
5004 }
5005 } else {
5006 // 'eval' is not bound in the global context. Just call the function
5007 // with the given arguments. This is not necessarily the global eval.
5008 if (receiver->IsContext()) {
5009 context = Handle<Context>::cast(receiver);
5010 receiver = Handle<Object>(context->get(index));
5011 }
5012 }
5013
5014 Handle<FixedArray> call = Factory::NewFixedArray(2);
5015 call->set(0, *callee);
5016 call->set(1, *receiver);
5017 return *call;
5018}
5019
5020
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005021static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5022 // This utility adjusts the property attributes for newly created Function
5023 // object ("new Function(...)") by changing the map.
5024 // All it does is changing the prototype property to enumerable
5025 // as specified in ECMA262, 15.3.5.2.
5026 HandleScope scope;
5027 ASSERT(args.length() == 1);
5028 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5029 ASSERT(func->map()->instance_type() ==
5030 Top::function_instance_map()->instance_type());
5031 ASSERT(func->map()->instance_size() ==
5032 Top::function_instance_map()->instance_size());
5033 func->set_map(*Top::function_instance_map());
5034 return *func;
5035}
5036
5037
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005038// Push an array unto an array of arrays if it is not already in the
5039// array. Returns true if the element was pushed on the stack and
5040// false otherwise.
5041static Object* Runtime_PushIfAbsent(Arguments args) {
5042 ASSERT(args.length() == 2);
5043 CONVERT_CHECKED(JSArray, array, args[0]);
5044 CONVERT_CHECKED(JSArray, element, args[1]);
5045 RUNTIME_ASSERT(array->HasFastElements());
5046 int length = Smi::cast(array->length())->value();
5047 FixedArray* elements = FixedArray::cast(array->elements());
5048 for (int i = 0; i < length; i++) {
5049 if (elements->get(i) == element) return Heap::false_value();
5050 }
5051 Object* obj = array->SetFastElement(length, element);
5052 if (obj->IsFailure()) return obj;
5053 return Heap::true_value();
5054}
5055
5056
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005057/**
5058 * A simple visitor visits every element of Array's.
5059 * The backend storage can be a fixed array for fast elements case,
5060 * or a dictionary for sparse array. Since Dictionary is a subtype
5061 * of FixedArray, the class can be used by both fast and slow cases.
5062 * The second parameter of the constructor, fast_elements, specifies
5063 * whether the storage is a FixedArray or Dictionary.
5064 *
5065 * An index limit is used to deal with the situation that a result array
5066 * length overflows 32-bit non-negative integer.
5067 */
5068class ArrayConcatVisitor {
5069 public:
5070 ArrayConcatVisitor(Handle<FixedArray> storage,
5071 uint32_t index_limit,
5072 bool fast_elements) :
5073 storage_(storage), index_limit_(index_limit),
5074 fast_elements_(fast_elements), index_offset_(0) { }
5075
5076 void visit(uint32_t i, Handle<Object> elm) {
5077 uint32_t index = i + index_offset_;
5078 if (index >= index_limit_) return;
5079
5080 if (fast_elements_) {
5081 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5082 storage_->set(index, *elm);
5083
5084 } else {
5085 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5086 Handle<Dictionary> result =
5087 Factory::DictionaryAtNumberPut(dict, index, elm);
5088 if (!result.is_identical_to(dict))
5089 storage_ = result;
5090 }
5091 }
5092
5093 void increase_index_offset(uint32_t delta) {
5094 index_offset_ += delta;
5095 }
5096
5097 private:
5098 Handle<FixedArray> storage_;
5099 uint32_t index_limit_;
5100 bool fast_elements_;
5101 uint32_t index_offset_;
5102};
5103
5104
5105/**
5106 * A helper function that visits elements of a JSObject. Only elements
5107 * whose index between 0 and range (exclusive) are visited.
5108 *
5109 * If the third parameter, visitor, is not NULL, the visitor is called
5110 * with parameters, 'visitor_index_offset + element index' and the element.
5111 *
5112 * It returns the number of visisted elements.
5113 */
5114static uint32_t IterateElements(Handle<JSObject> receiver,
5115 uint32_t range,
5116 ArrayConcatVisitor* visitor) {
5117 uint32_t num_of_elements = 0;
5118
5119 if (receiver->HasFastElements()) {
5120 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5121 uint32_t len = elements->length();
5122 if (range < len) len = range;
5123
5124 for (uint32_t j = 0; j < len; j++) {
5125 Handle<Object> e(elements->get(j));
5126 if (!e->IsTheHole()) {
5127 num_of_elements++;
5128 if (visitor)
5129 visitor->visit(j, e);
5130 }
5131 }
5132
5133 } else {
5134 Handle<Dictionary> dict(receiver->element_dictionary());
5135 uint32_t capacity = dict->Capacity();
5136 for (uint32_t j = 0; j < capacity; j++) {
5137 Handle<Object> k(dict->KeyAt(j));
5138 if (dict->IsKey(*k)) {
5139 ASSERT(k->IsNumber());
5140 uint32_t index = static_cast<uint32_t>(k->Number());
5141 if (index < range) {
5142 num_of_elements++;
5143 if (visitor) {
5144 visitor->visit(index,
5145 Handle<Object>(dict->ValueAt(j)));
5146 }
5147 }
5148 }
5149 }
5150 }
5151
5152 return num_of_elements;
5153}
5154
5155
5156/**
5157 * A helper function that visits elements of an Array object, and elements
5158 * on its prototypes.
5159 *
5160 * Elements on prototypes are visited first, and only elements whose indices
5161 * less than Array length are visited.
5162 *
5163 * If a ArrayConcatVisitor object is given, the visitor is called with
5164 * parameters, element's index + visitor_index_offset and the element.
5165 */
5166static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5167 ArrayConcatVisitor* visitor) {
5168 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5169 Handle<Object> obj = array;
5170
5171 static const int kEstimatedPrototypes = 3;
5172 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5173
5174 // Visit prototype first. If an element on the prototype is shadowed by
5175 // the inheritor using the same index, the ArrayConcatVisitor visits
5176 // the prototype element before the shadowing element.
5177 // The visitor can simply overwrite the old value by new value using
5178 // the same index. This follows Array::concat semantics.
5179 while (!obj->IsNull()) {
5180 objects.Add(Handle<JSObject>::cast(obj));
5181 obj = Handle<Object>(obj->GetPrototype());
5182 }
5183
5184 uint32_t nof_elements = 0;
5185 for (int i = objects.length() - 1; i >= 0; i--) {
5186 Handle<JSObject> obj = objects[i];
5187 nof_elements +=
5188 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5189 }
5190
5191 return nof_elements;
5192}
5193
5194
5195/**
5196 * A helper function of Runtime_ArrayConcat.
5197 *
5198 * The first argument is an Array of arrays and objects. It is the
5199 * same as the arguments array of Array::concat JS function.
5200 *
5201 * If an argument is an Array object, the function visits array
5202 * elements. If an argument is not an Array object, the function
5203 * visits the object as if it is an one-element array.
5204 *
5205 * If the result array index overflows 32-bit integer, the rounded
5206 * non-negative number is used as new length. For example, if one
5207 * array length is 2^32 - 1, second array length is 1, the
5208 * concatenated array length is 0.
5209 */
5210static uint32_t IterateArguments(Handle<JSArray> arguments,
5211 ArrayConcatVisitor* visitor) {
5212 uint32_t visited_elements = 0;
5213 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5214
5215 for (uint32_t i = 0; i < num_of_args; i++) {
5216 Handle<Object> obj(arguments->GetElement(i));
5217 if (obj->IsJSArray()) {
5218 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5219 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5220 uint32_t nof_elements =
5221 IterateArrayAndPrototypeElements(array, visitor);
5222 // Total elements of array and its prototype chain can be more than
5223 // the array length, but ArrayConcat can only concatenate at most
5224 // the array length number of elements.
5225 visited_elements += (nof_elements > len) ? len : nof_elements;
5226 if (visitor) visitor->increase_index_offset(len);
5227
5228 } else {
5229 if (visitor) {
5230 visitor->visit(0, obj);
5231 visitor->increase_index_offset(1);
5232 }
5233 visited_elements++;
5234 }
5235 }
5236 return visited_elements;
5237}
5238
5239
5240/**
5241 * Array::concat implementation.
5242 * See ECMAScript 262, 15.4.4.4.
5243 */
5244static Object* Runtime_ArrayConcat(Arguments args) {
5245 ASSERT(args.length() == 1);
5246 HandleScope handle_scope;
5247
5248 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5249 Handle<JSArray> arguments(arg_arrays);
5250
5251 // Pass 1: estimate the number of elements of the result
5252 // (it could be more than real numbers if prototype has elements).
5253 uint32_t result_length = 0;
5254 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5255
5256 { AssertNoAllocation nogc;
5257 for (uint32_t i = 0; i < num_of_args; i++) {
5258 Object* obj = arguments->GetElement(i);
5259 if (obj->IsJSArray()) {
5260 result_length +=
5261 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5262 } else {
5263 result_length++;
5264 }
5265 }
5266 }
5267
5268 // Allocate an empty array, will set length and content later.
5269 Handle<JSArray> result = Factory::NewJSArray(0);
5270
5271 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5272 // If estimated number of elements is more than half of length, a
5273 // fixed array (fast case) is more time and space-efficient than a
5274 // dictionary.
5275 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5276
5277 Handle<FixedArray> storage;
5278 if (fast_case) {
5279 // The backing storage array must have non-existing elements to
5280 // preserve holes across concat operations.
5281 storage = Factory::NewFixedArrayWithHoles(result_length);
5282
5283 } else {
5284 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5285 uint32_t at_least_space_for = estimate_nof_elements +
5286 (estimate_nof_elements >> 2);
5287 storage = Handle<FixedArray>::cast(
5288 Factory::NewDictionary(at_least_space_for));
5289 }
5290
5291 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5292
5293 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5294
5295 IterateArguments(arguments, &visitor);
5296
5297 result->set_length(*len);
5298 result->set_elements(*storage);
5299
5300 return *result;
5301}
5302
5303
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005304// This will not allocate (flatten the string), but it may run
5305// very slowly for very deeply nested ConsStrings. For debugging use only.
5306static Object* Runtime_GlobalPrint(Arguments args) {
5307 NoHandleAllocation ha;
5308 ASSERT(args.length() == 1);
5309
5310 CONVERT_CHECKED(String, string, args[0]);
5311 StringInputBuffer buffer(string);
5312 while (buffer.has_more()) {
5313 uint16_t character = buffer.GetNext();
5314 PrintF("%c", character);
5315 }
5316 return string;
5317}
5318
ager@chromium.org5ec48922009-05-05 07:25:34 +00005319// Moves all own elements of an object, that are below a limit, to positions
5320// starting at zero. All undefined values are placed after non-undefined values,
5321// and are followed by non-existing element. Does not change the length
5322// property.
5323// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005324static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005325 ASSERT(args.length() == 2);
5326 CONVERT_CHECKED(JSObject, object, args[0]);
5327 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5328 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005329}
5330
5331
5332// Move contents of argument 0 (an array) to argument 1 (an array)
5333static Object* Runtime_MoveArrayContents(Arguments args) {
5334 ASSERT(args.length() == 2);
5335 CONVERT_CHECKED(JSArray, from, args[0]);
5336 CONVERT_CHECKED(JSArray, to, args[1]);
5337 to->SetContent(FixedArray::cast(from->elements()));
5338 to->set_length(from->length());
5339 from->SetContent(Heap::empty_fixed_array());
5340 from->set_length(0);
5341 return to;
5342}
5343
5344
5345// How many elements does this array have?
5346static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5347 ASSERT(args.length() == 1);
5348 CONVERT_CHECKED(JSArray, array, args[0]);
5349 HeapObject* elements = array->elements();
5350 if (elements->IsDictionary()) {
5351 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5352 } else {
5353 return array->length();
5354 }
5355}
5356
5357
5358// Returns an array that tells you where in the [0, length) interval an array
5359// might have elements. Can either return keys or intervals. Keys can have
5360// gaps in (undefined). Intervals can also span over some undefined keys.
5361static Object* Runtime_GetArrayKeys(Arguments args) {
5362 ASSERT(args.length() == 2);
5363 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005364 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005365 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005366 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005367 // Create an array and get all the keys into it, then remove all the
5368 // keys that are not integers in the range 0 to length-1.
5369 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5370 int keys_length = keys->length();
5371 for (int i = 0; i < keys_length; i++) {
5372 Object* key = keys->get(i);
5373 uint32_t index;
5374 if (!Array::IndexFromObject(key, &index) || index >= length) {
5375 // Zap invalid keys.
5376 keys->set_undefined(i);
5377 }
5378 }
5379 return *Factory::NewJSArrayWithElements(keys);
5380 } else {
5381 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5382 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005383 single_interval->set(0,
5384 Smi::FromInt(-1),
5385 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005386 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5387 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005388 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005389 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005390 single_interval->set(1, *length_object);
5391 return *Factory::NewJSArrayWithElements(single_interval);
5392 }
5393}
5394
5395
5396// DefineAccessor takes an optional final argument which is the
5397// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5398// to the way accessors are implemented, it is set for both the getter
5399// and setter on the first call to DefineAccessor and ignored on
5400// subsequent calls.
5401static Object* Runtime_DefineAccessor(Arguments args) {
5402 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5403 // Compute attributes.
5404 PropertyAttributes attributes = NONE;
5405 if (args.length() == 5) {
5406 CONVERT_CHECKED(Smi, attrs, args[4]);
5407 int value = attrs->value();
5408 // Only attribute bits should be set.
5409 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5410 attributes = static_cast<PropertyAttributes>(value);
5411 }
5412
5413 CONVERT_CHECKED(JSObject, obj, args[0]);
5414 CONVERT_CHECKED(String, name, args[1]);
5415 CONVERT_CHECKED(Smi, flag, args[2]);
5416 CONVERT_CHECKED(JSFunction, fun, args[3]);
5417 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5418}
5419
5420
5421static Object* Runtime_LookupAccessor(Arguments args) {
5422 ASSERT(args.length() == 3);
5423 CONVERT_CHECKED(JSObject, obj, args[0]);
5424 CONVERT_CHECKED(String, name, args[1]);
5425 CONVERT_CHECKED(Smi, flag, args[2]);
5426 return obj->LookupAccessor(name, flag->value() == 0);
5427}
5428
5429
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005430#ifdef ENABLE_DEBUGGER_SUPPORT
5431static Object* Runtime_DebugBreak(Arguments args) {
5432 ASSERT(args.length() == 0);
5433 return Execution::DebugBreakHelper();
5434}
5435
5436
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005437// Helper functions for wrapping and unwrapping stack frame ids.
5438static Smi* WrapFrameId(StackFrame::Id id) {
5439 ASSERT(IsAligned(OffsetFrom(id), 4));
5440 return Smi::FromInt(id >> 2);
5441}
5442
5443
5444static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5445 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5446}
5447
5448
5449// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005450// args[0]: debug event listener function to set or null or undefined for
5451// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005452// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005453static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005454 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005455 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5456 args[0]->IsUndefined() ||
5457 args[0]->IsNull());
5458 Handle<Object> callback = args.at<Object>(0);
5459 Handle<Object> data = args.at<Object>(1);
5460 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005461
5462 return Heap::undefined_value();
5463}
5464
5465
5466static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005467 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005468 StackGuard::DebugBreak();
5469 return Heap::undefined_value();
5470}
5471
5472
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005473// Find the length of the prototype chain that is to to handled as one. If a
5474// prototype object is hidden it is to be viewed as part of the the object it
5475// is prototype for.
5476static int LocalPrototypeChainLength(JSObject* obj) {
5477 int count = 1;
5478 Object* proto = obj->GetPrototype();
5479 while (proto->IsJSObject() &&
5480 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5481 count++;
5482 proto = JSObject::cast(proto)->GetPrototype();
5483 }
5484 return count;
5485}
5486
5487
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005488static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005489 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005490 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005491 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005492 case NORMAL: {
5493 Dictionary* dict =
5494 JSObject::cast(result->holder())->property_dictionary();
5495 value = dict->ValueAt(result->GetDictionaryEntry());
5496 if (value->IsTheHole()) {
5497 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 }
5499 return value;
5500 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005501 case FIELD:
5502 value =
5503 JSObject::cast(
5504 result->holder())->FastPropertyAt(result->GetFieldIndex());
5505 if (value->IsTheHole()) {
5506 return Heap::undefined_value();
5507 }
5508 return value;
5509 case CONSTANT_FUNCTION:
5510 return result->GetConstantFunction();
5511 case CALLBACKS: {
5512 Object* structure = result->GetCallbackObject();
5513 if (structure->IsProxy()) {
5514 AccessorDescriptor* callback =
5515 reinterpret_cast<AccessorDescriptor*>(
5516 Proxy::cast(structure)->proxy());
5517 value = (callback->getter)(receiver, callback->data);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005518 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005519 value = Top::pending_exception();
5520 Top::clear_pending_exception();
5521 if (caught_exception != NULL) {
5522 *caught_exception = true;
5523 }
5524 }
5525 return value;
5526 } else {
5527 return Heap::undefined_value();
5528 }
5529 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005530 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005531 case MAP_TRANSITION:
5532 case CONSTANT_TRANSITION:
5533 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005534 return Heap::undefined_value();
5535 default:
5536 UNREACHABLE();
5537 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005538 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005539 return Heap::undefined_value();
5540}
5541
5542
ager@chromium.org32912102009-01-16 10:38:43 +00005543// Get debugger related details for an object property.
5544// args[0]: object holding property
5545// args[1]: name of the property
5546//
5547// The array returned contains the following information:
5548// 0: Property value
5549// 1: Property details
5550// 2: Property value is exception
5551// 3: Getter function if defined
5552// 4: Setter function if defined
5553// Items 2-4 are only filled if the property has either a getter or a setter
5554// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005555static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005556 HandleScope scope;
5557
5558 ASSERT(args.length() == 2);
5559
5560 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5561 CONVERT_ARG_CHECKED(String, name, 1);
5562
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005563 // Skip the global proxy as it has no properties and always delegates to the
5564 // real global object.
5565 if (obj->IsJSGlobalProxy()) {
5566 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5567 }
5568
5569
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005570 // Check if the name is trivially convertible to an index and get the element
5571 // if so.
5572 uint32_t index;
5573 if (name->AsArrayIndex(&index)) {
5574 Handle<FixedArray> details = Factory::NewFixedArray(2);
5575 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5576 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5577 return *Factory::NewJSArrayWithElements(details);
5578 }
5579
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005580 // Find the number of objects making up this.
5581 int length = LocalPrototypeChainLength(*obj);
5582
5583 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005584 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005585 Handle<JSObject> jsproto = obj;
5586 for (int i = 0; i < length; i++) {
5587 jsproto->LocalLookup(*name, &result);
5588 if (result.IsProperty()) {
5589 break;
5590 }
5591 if (i < length - 1) {
5592 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5593 }
5594 }
5595
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005596 if (result.IsProperty()) {
ager@chromium.org32912102009-01-16 10:38:43 +00005597 bool caught_exception = false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00005598 Object* value = DebugLookupResultValue(*obj, &result,
5599 &caught_exception);
5600 if (value->IsFailure()) return value;
5601 Handle<Object> value_handle(value);
ager@chromium.org32912102009-01-16 10:38:43 +00005602 // If the callback object is a fixed array then it contains JavaScript
5603 // getter and/or setter.
5604 bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
5605 result.GetCallbackObject()->IsFixedArray();
5606 Handle<FixedArray> details =
5607 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005608 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005609 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005610 if (hasJavaScriptAccessors) {
5611 details->set(2,
5612 caught_exception ? Heap::true_value() : Heap::false_value());
5613 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5614 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5615 }
5616
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005617 return *Factory::NewJSArrayWithElements(details);
5618 }
5619 return Heap::undefined_value();
5620}
5621
5622
5623static Object* Runtime_DebugGetProperty(Arguments args) {
5624 HandleScope scope;
5625
5626 ASSERT(args.length() == 2);
5627
5628 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5629 CONVERT_ARG_CHECKED(String, name, 1);
5630
5631 LookupResult result;
5632 obj->Lookup(*name, &result);
5633 if (result.IsProperty()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005634 return DebugLookupResultValue(*obj, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005635 }
5636 return Heap::undefined_value();
5637}
5638
5639
5640// Return the names of the local named properties.
5641// args[0]: object
5642static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5643 HandleScope scope;
5644 ASSERT(args.length() == 1);
5645 if (!args[0]->IsJSObject()) {
5646 return Heap::undefined_value();
5647 }
5648 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5649
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005650 // Skip the global proxy as it has no properties and always delegates to the
5651 // real global object.
5652 if (obj->IsJSGlobalProxy()) {
5653 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5654 }
5655
5656 // Find the number of objects making up this.
5657 int length = LocalPrototypeChainLength(*obj);
5658
5659 // Find the number of local properties for each of the objects.
5660 int* local_property_count = NewArray<int>(length);
5661 int total_property_count = 0;
5662 Handle<JSObject> jsproto = obj;
5663 for (int i = 0; i < length; i++) {
5664 int n;
5665 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5666 local_property_count[i] = n;
5667 total_property_count += n;
5668 if (i < length - 1) {
5669 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5670 }
5671 }
5672
5673 // Allocate an array with storage for all the property names.
5674 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5675
5676 // Get the property names.
5677 jsproto = obj;
5678 for (int i = 0; i < length; i++) {
5679 jsproto->GetLocalPropertyNames(*names,
5680 i == 0 ? 0 : local_property_count[i - 1]);
5681 if (i < length - 1) {
5682 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5683 }
5684 }
5685
5686 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005687 return *Factory::NewJSArrayWithElements(names);
5688}
5689
5690
5691// Return the names of the local indexed properties.
5692// args[0]: object
5693static Object* Runtime_DebugLocalElementNames(Arguments args) {
5694 HandleScope scope;
5695 ASSERT(args.length() == 1);
5696 if (!args[0]->IsJSObject()) {
5697 return Heap::undefined_value();
5698 }
5699 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5700
5701 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5702 Handle<FixedArray> names = Factory::NewFixedArray(n);
5703 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5704 return *Factory::NewJSArrayWithElements(names);
5705}
5706
5707
5708// Return the property type calculated from the property details.
5709// args[0]: smi with property details.
5710static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5711 ASSERT(args.length() == 1);
5712 CONVERT_CHECKED(Smi, details, args[0]);
5713 PropertyType type = PropertyDetails(details).type();
5714 return Smi::FromInt(static_cast<int>(type));
5715}
5716
5717
5718// Return the property attribute calculated from the property details.
5719// args[0]: smi with property details.
5720static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5721 ASSERT(args.length() == 1);
5722 CONVERT_CHECKED(Smi, details, args[0]);
5723 PropertyAttributes attributes = PropertyDetails(details).attributes();
5724 return Smi::FromInt(static_cast<int>(attributes));
5725}
5726
5727
5728// Return the property insertion index calculated from the property details.
5729// args[0]: smi with property details.
5730static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5731 ASSERT(args.length() == 1);
5732 CONVERT_CHECKED(Smi, details, args[0]);
5733 int index = PropertyDetails(details).index();
5734 return Smi::FromInt(index);
5735}
5736
5737
5738// Return information on whether an object has a named or indexed interceptor.
5739// args[0]: object
5740static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5741 HandleScope scope;
5742 ASSERT(args.length() == 1);
5743 if (!args[0]->IsJSObject()) {
5744 return Smi::FromInt(0);
5745 }
5746 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5747
5748 int result = 0;
5749 if (obj->HasNamedInterceptor()) result |= 2;
5750 if (obj->HasIndexedInterceptor()) result |= 1;
5751
5752 return Smi::FromInt(result);
5753}
5754
5755
5756// Return property names from named interceptor.
5757// args[0]: object
5758static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5759 HandleScope scope;
5760 ASSERT(args.length() == 1);
5761 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005762
ager@chromium.org32912102009-01-16 10:38:43 +00005763 if (obj->HasNamedInterceptor()) {
5764 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5765 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5766 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005767 return Heap::undefined_value();
5768}
5769
5770
5771// Return element names from indexed interceptor.
5772// args[0]: object
5773static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5774 HandleScope scope;
5775 ASSERT(args.length() == 1);
5776 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005777
ager@chromium.org32912102009-01-16 10:38:43 +00005778 if (obj->HasIndexedInterceptor()) {
5779 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5780 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5781 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005782 return Heap::undefined_value();
5783}
5784
5785
5786// Return property value from named interceptor.
5787// args[0]: object
5788// args[1]: property name
5789static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5790 HandleScope scope;
5791 ASSERT(args.length() == 2);
5792 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5793 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5794 CONVERT_ARG_CHECKED(String, name, 1);
5795
5796 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005797 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798}
5799
5800
5801// Return element value from indexed interceptor.
5802// args[0]: object
5803// args[1]: index
5804static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5805 HandleScope scope;
5806 ASSERT(args.length() == 2);
5807 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5808 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5809 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5810
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005811 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005812}
5813
5814
5815static Object* Runtime_CheckExecutionState(Arguments args) {
5816 ASSERT(args.length() >= 1);
5817 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005818 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005819 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005820 return Top::Throw(Heap::illegal_execution_state_symbol());
5821 }
5822
5823 return Heap::true_value();
5824}
5825
5826
5827static Object* Runtime_GetFrameCount(Arguments args) {
5828 HandleScope scope;
5829 ASSERT(args.length() == 1);
5830
5831 // Check arguments.
5832 Object* result = Runtime_CheckExecutionState(args);
5833 if (result->IsFailure()) return result;
5834
5835 // Count all frames which are relevant to debugging stack trace.
5836 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005837 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005838 if (id == StackFrame::NO_ID) {
5839 // If there is no JavaScript stack frame count is 0.
5840 return Smi::FromInt(0);
5841 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5843 return Smi::FromInt(n);
5844}
5845
5846
5847static const int kFrameDetailsFrameIdIndex = 0;
5848static const int kFrameDetailsReceiverIndex = 1;
5849static const int kFrameDetailsFunctionIndex = 2;
5850static const int kFrameDetailsArgumentCountIndex = 3;
5851static const int kFrameDetailsLocalCountIndex = 4;
5852static const int kFrameDetailsSourcePositionIndex = 5;
5853static const int kFrameDetailsConstructCallIndex = 6;
5854static const int kFrameDetailsDebuggerFrameIndex = 7;
5855static const int kFrameDetailsFirstDynamicIndex = 8;
5856
5857// Return an array with frame details
5858// args[0]: number: break id
5859// args[1]: number: frame index
5860//
5861// The array returned contains the following information:
5862// 0: Frame id
5863// 1: Receiver
5864// 2: Function
5865// 3: Argument count
5866// 4: Local count
5867// 5: Source position
5868// 6: Constructor call
5869// 7: Debugger frame
5870// Arguments name, value
5871// Locals name, value
5872static Object* Runtime_GetFrameDetails(Arguments args) {
5873 HandleScope scope;
5874 ASSERT(args.length() == 2);
5875
5876 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005877 Object* check = Runtime_CheckExecutionState(args);
5878 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005879 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5880
5881 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005882 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005883 if (id == StackFrame::NO_ID) {
5884 // If there are no JavaScript stack frames return undefined.
5885 return Heap::undefined_value();
5886 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887 int count = 0;
5888 JavaScriptFrameIterator it(id);
5889 for (; !it.done(); it.Advance()) {
5890 if (count == index) break;
5891 count++;
5892 }
5893 if (it.done()) return Heap::undefined_value();
5894
5895 // Traverse the saved contexts chain to find the active context for the
5896 // selected frame.
5897 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005898 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005899 save = save->prev();
5900 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005901 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005902
5903 // Get the frame id.
5904 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5905
5906 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005907 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005908
5909 // Check for constructor frame.
5910 bool constructor = it.frame()->IsConstructor();
5911
5912 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005913 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005914 ScopeInfo<> info(*code);
5915
5916 // Get the context.
5917 Handle<Context> context(Context::cast(it.frame()->context()));
5918
5919 // Get the locals names and values into a temporary array.
5920 //
5921 // TODO(1240907): Hide compiler-introduced stack variables
5922 // (e.g. .result)? For users of the debugger, they will probably be
5923 // confusing.
5924 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5925 for (int i = 0; i < info.NumberOfLocals(); i++) {
5926 // Name of the local.
5927 locals->set(i * 2, *info.LocalName(i));
5928
5929 // Fetch the value of the local - either from the stack or from a
5930 // heap-allocated context.
5931 if (i < info.number_of_stack_slots()) {
5932 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
5933 } else {
5934 Handle<String> name = info.LocalName(i);
5935 // Traverse the context chain to the function context as all local
5936 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005937 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005938 context = Handle<Context>(context->previous());
5939 }
5940 ASSERT(context->is_function_context());
5941 locals->set(i * 2 + 1,
5942 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
5943 NULL)));
5944 }
5945 }
5946
5947 // Now advance to the arguments adapter frame (if any). If contains all
5948 // the provided parameters and
5949
5950 // Now advance to the arguments adapter frame (if any). It contains all
5951 // the provided parameters whereas the function frame always have the number
5952 // of arguments matching the functions parameters. The rest of the
5953 // information (except for what is collected above) is the same.
5954 it.AdvanceToArgumentsFrame();
5955
5956 // Find the number of arguments to fill. At least fill the number of
5957 // parameters for the function and fill more if more parameters are provided.
5958 int argument_count = info.number_of_parameters();
5959 if (argument_count < it.frame()->GetProvidedParametersCount()) {
5960 argument_count = it.frame()->GetProvidedParametersCount();
5961 }
5962
5963 // Calculate the size of the result.
5964 int details_size = kFrameDetailsFirstDynamicIndex +
5965 2 * (argument_count + info.NumberOfLocals());
5966 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
5967
5968 // Add the frame id.
5969 details->set(kFrameDetailsFrameIdIndex, *frame_id);
5970
5971 // Add the function (same as in function frame).
5972 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
5973
5974 // Add the arguments count.
5975 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
5976
5977 // Add the locals count
5978 details->set(kFrameDetailsLocalCountIndex,
5979 Smi::FromInt(info.NumberOfLocals()));
5980
5981 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00005982 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005983 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
5984 } else {
5985 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
5986 }
5987
5988 // Add the constructor information.
5989 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
5990
5991 // Add information on whether this frame is invoked in the debugger context.
5992 details->set(kFrameDetailsDebuggerFrameIndex,
5993 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
5994
5995 // Fill the dynamic part.
5996 int details_index = kFrameDetailsFirstDynamicIndex;
5997
5998 // Add arguments name and value.
5999 for (int i = 0; i < argument_count; i++) {
6000 // Name of the argument.
6001 if (i < info.number_of_parameters()) {
6002 details->set(details_index++, *info.parameter_name(i));
6003 } else {
6004 details->set(details_index++, Heap::undefined_value());
6005 }
6006
6007 // Parameter value.
6008 if (i < it.frame()->GetProvidedParametersCount()) {
6009 details->set(details_index++, it.frame()->GetParameter(i));
6010 } else {
6011 details->set(details_index++, Heap::undefined_value());
6012 }
6013 }
6014
6015 // Add locals name and value from the temporary copy from the function frame.
6016 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6017 details->set(details_index++, locals->get(i));
6018 }
6019
6020 // Add the receiver (same as in function frame).
6021 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6022 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6023 Handle<Object> receiver(it.frame()->receiver());
6024 if (!receiver->IsJSObject()) {
6025 // If the receiver is NOT a JSObject we have hit an optimization
6026 // where a value object is not converted into a wrapped JS objects.
6027 // To hide this optimization from the debugger, we wrap the receiver
6028 // by creating correct wrapper object based on the calling frame's
6029 // global context.
6030 it.Advance();
6031 Handle<Context> calling_frames_global_context(
6032 Context::cast(Context::cast(it.frame()->context())->global_context()));
6033 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6034 }
6035 details->set(kFrameDetailsReceiverIndex, *receiver);
6036
6037 ASSERT_EQ(details_size, details_index);
6038 return *Factory::NewJSArrayWithElements(details);
6039}
6040
6041
6042static Object* Runtime_GetCFrames(Arguments args) {
6043 HandleScope scope;
6044 ASSERT(args.length() == 1);
6045 Object* result = Runtime_CheckExecutionState(args);
6046 if (result->IsFailure()) return result;
6047
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006048#if V8_HOST_ARCH_64_BIT
6049 UNIMPLEMENTED();
6050 return Heap::undefined_value();
6051#else
6052
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006053 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006054 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6055 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006056 if (frames_count == OS::kStackWalkError) {
6057 return Heap::undefined_value();
6058 }
6059
6060 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6061 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6062 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6063 for (int i = 0; i < frames_count; i++) {
6064 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6065 frame_value->SetProperty(
6066 *address_str,
6067 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6068 NONE);
6069
6070 // Get the stack walk text for this frame.
6071 Handle<String> frame_text;
6072 if (strlen(frames[i].text) > 0) {
6073 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6074 frame_text = Factory::NewStringFromAscii(str);
6075 }
6076
6077 if (!frame_text.is_null()) {
6078 frame_value->SetProperty(*text_str, *frame_text, NONE);
6079 }
6080
6081 frames_array->set(i, *frame_value);
6082 }
6083 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006084#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006085}
6086
6087
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006088static Object* Runtime_GetThreadCount(Arguments args) {
6089 HandleScope scope;
6090 ASSERT(args.length() == 1);
6091
6092 // Check arguments.
6093 Object* result = Runtime_CheckExecutionState(args);
6094 if (result->IsFailure()) return result;
6095
6096 // Count all archived V8 threads.
6097 int n = 0;
6098 for (ThreadState* thread = ThreadState::FirstInUse();
6099 thread != NULL;
6100 thread = thread->Next()) {
6101 n++;
6102 }
6103
6104 // Total number of threads is current thread and archived threads.
6105 return Smi::FromInt(n + 1);
6106}
6107
6108
6109static const int kThreadDetailsCurrentThreadIndex = 0;
6110static const int kThreadDetailsThreadIdIndex = 1;
6111static const int kThreadDetailsSize = 2;
6112
6113// Return an array with thread details
6114// args[0]: number: break id
6115// args[1]: number: thread index
6116//
6117// The array returned contains the following information:
6118// 0: Is current thread?
6119// 1: Thread id
6120static Object* Runtime_GetThreadDetails(Arguments args) {
6121 HandleScope scope;
6122 ASSERT(args.length() == 2);
6123
6124 // Check arguments.
6125 Object* check = Runtime_CheckExecutionState(args);
6126 if (check->IsFailure()) return check;
6127 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6128
6129 // Allocate array for result.
6130 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6131
6132 // Thread index 0 is current thread.
6133 if (index == 0) {
6134 // Fill the details.
6135 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6136 details->set(kThreadDetailsThreadIdIndex,
6137 Smi::FromInt(ThreadManager::CurrentId()));
6138 } else {
6139 // Find the thread with the requested index.
6140 int n = 1;
6141 ThreadState* thread = ThreadState::FirstInUse();
6142 while (index != n && thread != NULL) {
6143 thread = thread->Next();
6144 n++;
6145 }
6146 if (thread == NULL) {
6147 return Heap::undefined_value();
6148 }
6149
6150 // Fill the details.
6151 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6152 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6153 }
6154
6155 // Convert to JS array and return.
6156 return *Factory::NewJSArrayWithElements(details);
6157}
6158
6159
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006160static Object* Runtime_GetBreakLocations(Arguments args) {
6161 HandleScope scope;
6162 ASSERT(args.length() == 1);
6163
6164 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6165 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6166 // Find the number of break points
6167 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6168 if (break_locations->IsUndefined()) return Heap::undefined_value();
6169 // Return array as JS array
6170 return *Factory::NewJSArrayWithElements(
6171 Handle<FixedArray>::cast(break_locations));
6172}
6173
6174
6175// Set a break point in a function
6176// args[0]: function
6177// args[1]: number: break source position (within the function source)
6178// args[2]: number: break point object
6179static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6180 HandleScope scope;
6181 ASSERT(args.length() == 3);
6182 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6183 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6184 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6185 RUNTIME_ASSERT(source_position >= 0);
6186 Handle<Object> break_point_object_arg = args.at<Object>(2);
6187
6188 // Set break point.
6189 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6190
6191 return Heap::undefined_value();
6192}
6193
6194
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006195Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6196 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006197 // Iterate the heap looking for SharedFunctionInfo generated from the
6198 // script. The inner most SharedFunctionInfo containing the source position
6199 // for the requested break point is found.
6200 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6201 // which is found is not compiled it is compiled and the heap is iterated
6202 // again as the compilation might create inner functions from the newly
6203 // compiled function and the actual requested break point might be in one of
6204 // these functions.
6205 bool done = false;
6206 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006207 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006208 Handle<SharedFunctionInfo> target;
6209 // The current candidate for the last function in script:
6210 Handle<SharedFunctionInfo> last;
6211 while (!done) {
6212 HeapIterator iterator;
6213 while (iterator.has_next()) {
6214 HeapObject* obj = iterator.next();
6215 ASSERT(obj != NULL);
6216 if (obj->IsSharedFunctionInfo()) {
6217 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6218 if (shared->script() == *script) {
6219 // If the SharedFunctionInfo found has the requested script data and
6220 // contains the source position it is a candidate.
6221 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006222 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006223 start_position = shared->start_position();
6224 }
6225 if (start_position <= position &&
6226 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006227 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006228 // candidate this is the new candidate.
6229 if (target.is_null()) {
6230 target_start_position = start_position;
6231 target = shared;
6232 } else {
6233 if (target_start_position < start_position &&
6234 shared->end_position() < target->end_position()) {
6235 target_start_position = start_position;
6236 target = shared;
6237 }
6238 }
6239 }
6240
6241 // Keep track of the last function in the script.
6242 if (last.is_null() ||
6243 shared->end_position() > last->start_position()) {
6244 last = shared;
6245 }
6246 }
6247 }
6248 }
6249
6250 // Make sure some candidate is selected.
6251 if (target.is_null()) {
6252 if (!last.is_null()) {
6253 // Position after the last function - use last.
6254 target = last;
6255 } else {
6256 // Unable to find function - possibly script without any function.
6257 return Heap::undefined_value();
6258 }
6259 }
6260
6261 // If the candidate found is compiled we are done. NOTE: when lazy
6262 // compilation of inner functions is introduced some additional checking
6263 // needs to be done here to compile inner functions.
6264 done = target->is_compiled();
6265 if (!done) {
6266 // If the candidate is not compiled compile it to reveal any inner
6267 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006268 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006269 }
6270 }
6271
6272 return *target;
6273}
6274
6275
6276// Change the state of a break point in a script. NOTE: Regarding performance
6277// see the NOTE for GetScriptFromScriptData.
6278// args[0]: script to set break point in
6279// args[1]: number: break source position (within the script source)
6280// args[2]: number: break point object
6281static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6282 HandleScope scope;
6283 ASSERT(args.length() == 3);
6284 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6285 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6286 RUNTIME_ASSERT(source_position >= 0);
6287 Handle<Object> break_point_object_arg = args.at<Object>(2);
6288
6289 // Get the script from the script wrapper.
6290 RUNTIME_ASSERT(wrapper->value()->IsScript());
6291 Handle<Script> script(Script::cast(wrapper->value()));
6292
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006293 Object* result = Runtime::FindSharedFunctionInfoInScript(
6294 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006295 if (!result->IsUndefined()) {
6296 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6297 // Find position within function. The script position might be before the
6298 // source position of the first function.
6299 int position;
6300 if (shared->start_position() > source_position) {
6301 position = 0;
6302 } else {
6303 position = source_position - shared->start_position();
6304 }
6305 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6306 }
6307 return Heap::undefined_value();
6308}
6309
6310
6311// Clear a break point
6312// args[0]: number: break point object
6313static Object* Runtime_ClearBreakPoint(Arguments args) {
6314 HandleScope scope;
6315 ASSERT(args.length() == 1);
6316 Handle<Object> break_point_object_arg = args.at<Object>(0);
6317
6318 // Clear break point.
6319 Debug::ClearBreakPoint(break_point_object_arg);
6320
6321 return Heap::undefined_value();
6322}
6323
6324
6325// Change the state of break on exceptions
6326// args[0]: boolean indicating uncaught exceptions
6327// args[1]: boolean indicating on/off
6328static Object* Runtime_ChangeBreakOnException(Arguments args) {
6329 HandleScope scope;
6330 ASSERT(args.length() == 2);
6331 ASSERT(args[0]->IsNumber());
6332 ASSERT(args[1]->IsBoolean());
6333
6334 // Update break point state
6335 ExceptionBreakType type =
6336 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6337 bool enable = args[1]->ToBoolean()->IsTrue();
6338 Debug::ChangeBreakOnException(type, enable);
6339 return Heap::undefined_value();
6340}
6341
6342
6343// Prepare for stepping
6344// args[0]: break id for checking execution state
6345// args[1]: step action from the enumeration StepAction
6346// args[2]: number of times to perform the step
6347static Object* Runtime_PrepareStep(Arguments args) {
6348 HandleScope scope;
6349 ASSERT(args.length() == 3);
6350 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006351 Object* check = Runtime_CheckExecutionState(args);
6352 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006353 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6354 return Top::Throw(Heap::illegal_argument_symbol());
6355 }
6356
6357 // Get the step action and check validity.
6358 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6359 if (step_action != StepIn &&
6360 step_action != StepNext &&
6361 step_action != StepOut &&
6362 step_action != StepInMin &&
6363 step_action != StepMin) {
6364 return Top::Throw(Heap::illegal_argument_symbol());
6365 }
6366
6367 // Get the number of steps.
6368 int step_count = NumberToInt32(args[2]);
6369 if (step_count < 1) {
6370 return Top::Throw(Heap::illegal_argument_symbol());
6371 }
6372
6373 // Prepare step.
6374 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6375 return Heap::undefined_value();
6376}
6377
6378
6379// Clear all stepping set by PrepareStep.
6380static Object* Runtime_ClearStepping(Arguments args) {
6381 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006382 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006383 Debug::ClearStepping();
6384 return Heap::undefined_value();
6385}
6386
6387
6388// Creates a copy of the with context chain. The copy of the context chain is
6389// is linked to the function context supplied.
6390static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6391 Handle<Context> function_context) {
6392 // At the bottom of the chain. Return the function context to link to.
6393 if (context_chain->is_function_context()) {
6394 return function_context;
6395 }
6396
6397 // Recursively copy the with contexts.
6398 Handle<Context> previous(context_chain->previous());
6399 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6400 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006401 CopyWithContextChain(function_context, previous),
6402 extension,
6403 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006404}
6405
6406
6407// Helper function to find or create the arguments object for
6408// Runtime_DebugEvaluate.
6409static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6410 Handle<JSFunction> function,
6411 Handle<Code> code,
6412 const ScopeInfo<>* sinfo,
6413 Handle<Context> function_context) {
6414 // Try to find the value of 'arguments' to pass as parameter. If it is not
6415 // found (that is the debugged function does not reference 'arguments' and
6416 // does not support eval) then create an 'arguments' object.
6417 int index;
6418 if (sinfo->number_of_stack_slots() > 0) {
6419 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6420 if (index != -1) {
6421 return Handle<Object>(frame->GetExpression(index));
6422 }
6423 }
6424
6425 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6426 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6427 NULL);
6428 if (index != -1) {
6429 return Handle<Object>(function_context->get(index));
6430 }
6431 }
6432
6433 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006434 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6435 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006436 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006437 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006438 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006439 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006440 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006441 return arguments;
6442}
6443
6444
6445// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006446// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006447// extension part has all the parameters and locals of the function on the
6448// stack frame. A function which calls eval with the code to evaluate is then
6449// compiled in this context and called in this context. As this context
6450// replaces the context of the function on the stack frame a new (empty)
6451// function is created as well to be used as the closure for the context.
6452// This function and the context acts as replacements for the function on the
6453// stack frame presenting the same view of the values of parameters and
6454// local variables as if the piece of JavaScript was evaluated at the point
6455// where the function on the stack frame is currently stopped.
6456static Object* Runtime_DebugEvaluate(Arguments args) {
6457 HandleScope scope;
6458
6459 // Check the execution state and decode arguments frame and source to be
6460 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006461 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006462 Object* check_result = Runtime_CheckExecutionState(args);
6463 if (check_result->IsFailure()) return check_result;
6464 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6465 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006466 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6467
6468 // Handle the processing of break.
6469 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006470
6471 // Get the frame where the debugging is performed.
6472 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6473 JavaScriptFrameIterator it(id);
6474 JavaScriptFrame* frame = it.frame();
6475 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6476 Handle<Code> code(function->code());
6477 ScopeInfo<> sinfo(*code);
6478
6479 // Traverse the saved contexts chain to find the active context for the
6480 // selected frame.
6481 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006482 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006483 save = save->prev();
6484 }
6485 ASSERT(save != NULL);
6486 SaveContext savex;
6487 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006488
6489 // Create the (empty) function replacing the function on the stack frame for
6490 // the purpose of evaluating in the context created below. It is important
6491 // that this function does not describe any parameters and local variables
6492 // in the context. If it does then this will cause problems with the lookup
6493 // in Context::Lookup, where context slots for parameters and local variables
6494 // are looked at before the extension object.
6495 Handle<JSFunction> go_between =
6496 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6497 go_between->set_context(function->context());
6498#ifdef DEBUG
6499 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6500 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6501 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6502#endif
6503
6504 // Allocate and initialize a context extension object with all the
6505 // arguments, stack locals heap locals and extension properties of the
6506 // debugged function.
6507 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6508 // First fill all parameters to the context extension.
6509 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6510 SetProperty(context_ext,
6511 sinfo.parameter_name(i),
6512 Handle<Object>(frame->GetParameter(i)), NONE);
6513 }
6514 // Second fill all stack locals to the context extension.
6515 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6516 SetProperty(context_ext,
6517 sinfo.stack_slot_name(i),
6518 Handle<Object>(frame->GetExpression(i)), NONE);
6519 }
6520 // Third fill all context locals to the context extension.
6521 Handle<Context> frame_context(Context::cast(frame->context()));
6522 Handle<Context> function_context(frame_context->fcontext());
6523 for (int i = Context::MIN_CONTEXT_SLOTS;
6524 i < sinfo.number_of_context_slots();
6525 ++i) {
6526 int context_index =
6527 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6528 SetProperty(context_ext,
6529 sinfo.context_slot_name(i),
6530 Handle<Object>(function_context->get(context_index)), NONE);
6531 }
6532 // Finally copy any properties from the function context extension. This will
6533 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006534 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006535 !function_context->IsGlobalContext()) {
6536 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6537 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6538 for (int i = 0; i < keys->length(); i++) {
6539 // Names of variables introduced by eval are strings.
6540 ASSERT(keys->get(i)->IsString());
6541 Handle<String> key(String::cast(keys->get(i)));
6542 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6543 }
6544 }
6545
6546 // Allocate a new context for the debug evaluation and set the extension
6547 // object build.
6548 Handle<Context> context =
6549 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6550 context->set_extension(*context_ext);
6551 // Copy any with contexts present and chain them in front of this context.
6552 context = CopyWithContextChain(frame_context, context);
6553
6554 // Wrap the evaluation statement in a new function compiled in the newly
6555 // created context. The function has one parameter which has to be called
6556 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006557 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006558 // function(arguments,__source__) {return eval(__source__);}
6559 static const char* source_str =
6560 "function(arguments,__source__){return eval(__source__);}";
6561 static const int source_str_length = strlen(source_str);
6562 Handle<String> function_source =
6563 Factory::NewStringFromAscii(Vector<const char>(source_str,
6564 source_str_length));
6565 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006566 Compiler::CompileEval(function_source,
6567 context,
6568 0,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006569 context->IsGlobalContext(),
6570 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006571 if (boilerplate.is_null()) return Failure::Exception();
6572 Handle<JSFunction> compiled_function =
6573 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6574
6575 // Invoke the result of the compilation to get the evaluation function.
6576 bool has_pending_exception;
6577 Handle<Object> receiver(frame->receiver());
6578 Handle<Object> evaluation_function =
6579 Execution::Call(compiled_function, receiver, 0, NULL,
6580 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006581 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006582
6583 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6584 function_context);
6585
6586 // Invoke the evaluation function and return the result.
6587 const int argc = 2;
6588 Object** argv[argc] = { arguments.location(),
6589 Handle<Object>::cast(source).location() };
6590 Handle<Object> result =
6591 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6592 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006593 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006594 return *result;
6595}
6596
6597
6598static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6599 HandleScope scope;
6600
6601 // Check the execution state and decode arguments frame and source to be
6602 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006603 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006604 Object* check_result = Runtime_CheckExecutionState(args);
6605 if (check_result->IsFailure()) return check_result;
6606 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006607 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6608
6609 // Handle the processing of break.
6610 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006611
6612 // Enter the top context from before the debugger was invoked.
6613 SaveContext save;
6614 SaveContext* top = &save;
6615 while (top != NULL && *top->context() == *Debug::debug_context()) {
6616 top = top->prev();
6617 }
6618 if (top != NULL) {
6619 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006620 }
6621
6622 // Get the global context now set to the top context from before the
6623 // debugger was invoked.
6624 Handle<Context> context = Top::global_context();
6625
6626 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006627 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006628 Handle<JSFunction>(Compiler::CompileEval(source,
6629 context,
6630 0,
6631 true,
6632 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006633 if (boilerplate.is_null()) return Failure::Exception();
6634 Handle<JSFunction> compiled_function =
6635 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6636 context));
6637
6638 // Invoke the result of the compilation to get the evaluation function.
6639 bool has_pending_exception;
6640 Handle<Object> receiver = Top::global();
6641 Handle<Object> result =
6642 Execution::Call(compiled_function, receiver, 0, NULL,
6643 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006644 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006645 return *result;
6646}
6647
6648
ager@chromium.org41826e72009-03-30 13:30:57 +00006649// If an object given is an external string, check that the underlying
6650// resource is accessible. For other kinds of objects, always return true.
6651static bool IsExternalStringValid(Object* str) {
6652 if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) {
6653 return true;
6654 }
ager@chromium.org5ec48922009-05-05 07:25:34 +00006655 if (String::cast(str)->IsAsciiRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006656 return ExternalAsciiString::cast(str)->resource() != NULL;
ager@chromium.org5ec48922009-05-05 07:25:34 +00006657 } else if (String::cast(str)->IsTwoByteRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006658 return ExternalTwoByteString::cast(str)->resource() != NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00006659 } else {
6660 return true;
6661 }
6662}
6663
6664
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006665// Helper function used by Runtime_DebugGetLoadedScripts below.
6666static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
6667 NoHandleAllocation ha;
6668 AssertNoAllocation no_alloc;
6669
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006670 // Scan heap for Script objects.
6671 int count = 0;
6672 HeapIterator iterator;
6673 while (iterator.has_next()) {
6674 HeapObject* obj = iterator.next();
6675 ASSERT(obj != NULL);
ager@chromium.org41826e72009-03-30 13:30:57 +00006676 if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006677 if (instances != NULL && count < instances_size) {
6678 instances->set(count, obj);
6679 }
6680 count++;
6681 }
6682 }
6683
6684 return count;
6685}
6686
6687
6688static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6689 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006690 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006691
6692 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
ager@chromium.org32912102009-01-16 10:38:43 +00006693 // rid of all the cached script wrappers and the second gets rid of the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006694 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006695 Heap::CollectAllGarbage();
6696 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006697
6698 // Get the number of scripts.
6699 int count;
6700 count = DebugGetLoadedScripts(NULL, 0);
6701
6702 // Allocate an array to hold the result.
6703 Handle<FixedArray> instances = Factory::NewFixedArray(count);
6704
6705 // Fill the script objects.
6706 count = DebugGetLoadedScripts(*instances, count);
6707
6708 // Convert the script objects to proper JS objects.
6709 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006710 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6711 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6712 // because using
6713 // instances->set(i, *GetScriptWrapper(script))
6714 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6715 // already have deferenced the instances handle.
6716 Handle<JSValue> wrapper = GetScriptWrapper(script);
6717 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006718 }
6719
6720 // Return result as a JS array.
6721 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6722 Handle<JSArray>::cast(result)->SetContent(*instances);
6723 return *result;
6724}
6725
6726
6727// Helper function used by Runtime_DebugReferencedBy below.
6728static int DebugReferencedBy(JSObject* target,
6729 Object* instance_filter, int max_references,
6730 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006731 JSFunction* arguments_function) {
6732 NoHandleAllocation ha;
6733 AssertNoAllocation no_alloc;
6734
6735 // Iterate the heap.
6736 int count = 0;
6737 JSObject* last = NULL;
6738 HeapIterator iterator;
6739 while (iterator.has_next() &&
6740 (max_references == 0 || count < max_references)) {
6741 // Only look at all JSObjects.
6742 HeapObject* heap_obj = iterator.next();
6743 if (heap_obj->IsJSObject()) {
6744 // Skip context extension objects and argument arrays as these are
6745 // checked in the context of functions using them.
6746 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006747 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006748 obj->map()->constructor() == arguments_function) {
6749 continue;
6750 }
6751
6752 // Check if the JS object has a reference to the object looked for.
6753 if (obj->ReferencesObject(target)) {
6754 // Check instance filter if supplied. This is normally used to avoid
6755 // references from mirror objects (see Runtime_IsInPrototypeChain).
6756 if (!instance_filter->IsUndefined()) {
6757 Object* V = obj;
6758 while (true) {
6759 Object* prototype = V->GetPrototype();
6760 if (prototype->IsNull()) {
6761 break;
6762 }
6763 if (instance_filter == prototype) {
6764 obj = NULL; // Don't add this object.
6765 break;
6766 }
6767 V = prototype;
6768 }
6769 }
6770
6771 if (obj != NULL) {
6772 // Valid reference found add to instance array if supplied an update
6773 // count.
6774 if (instances != NULL && count < instances_size) {
6775 instances->set(count, obj);
6776 }
6777 last = obj;
6778 count++;
6779 }
6780 }
6781 }
6782 }
6783
6784 // Check for circular reference only. This can happen when the object is only
6785 // referenced from mirrors and has a circular reference in which case the
6786 // object is not really alive and would have been garbage collected if not
6787 // referenced from the mirror.
6788 if (count == 1 && last == target) {
6789 count = 0;
6790 }
6791
6792 // Return the number of referencing objects found.
6793 return count;
6794}
6795
6796
6797// Scan the heap for objects with direct references to an object
6798// args[0]: the object to find references to
6799// args[1]: constructor function for instances to exclude (Mirror)
6800// args[2]: the the maximum number of objects to return
6801static Object* Runtime_DebugReferencedBy(Arguments args) {
6802 ASSERT(args.length() == 3);
6803
6804 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006805 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006806
6807 // Check parameters.
6808 CONVERT_CHECKED(JSObject, target, args[0]);
6809 Object* instance_filter = args[1];
6810 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6811 instance_filter->IsJSObject());
6812 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6813 RUNTIME_ASSERT(max_references >= 0);
6814
6815 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006816 JSObject* arguments_boilerplate =
6817 Top::context()->global_context()->arguments_boilerplate();
6818 JSFunction* arguments_function =
6819 JSFunction::cast(arguments_boilerplate->map()->constructor());
6820
6821 // Get the number of referencing objects.
6822 int count;
6823 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006824 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006825
6826 // Allocate an array to hold the result.
6827 Object* object = Heap::AllocateFixedArray(count);
6828 if (object->IsFailure()) return object;
6829 FixedArray* instances = FixedArray::cast(object);
6830
6831 // Fill the referencing objects.
6832 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006833 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006834
6835 // Return result as JS array.
6836 Object* result =
6837 Heap::AllocateJSObject(
6838 Top::context()->global_context()->array_function());
6839 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6840 return result;
6841}
6842
6843
6844// Helper function used by Runtime_DebugConstructedBy below.
6845static int DebugConstructedBy(JSFunction* constructor, int max_references,
6846 FixedArray* instances, int instances_size) {
6847 AssertNoAllocation no_alloc;
6848
6849 // Iterate the heap.
6850 int count = 0;
6851 HeapIterator iterator;
6852 while (iterator.has_next() &&
6853 (max_references == 0 || count < max_references)) {
6854 // Only look at all JSObjects.
6855 HeapObject* heap_obj = iterator.next();
6856 if (heap_obj->IsJSObject()) {
6857 JSObject* obj = JSObject::cast(heap_obj);
6858 if (obj->map()->constructor() == constructor) {
6859 // Valid reference found add to instance array if supplied an update
6860 // count.
6861 if (instances != NULL && count < instances_size) {
6862 instances->set(count, obj);
6863 }
6864 count++;
6865 }
6866 }
6867 }
6868
6869 // Return the number of referencing objects found.
6870 return count;
6871}
6872
6873
6874// Scan the heap for objects constructed by a specific function.
6875// args[0]: the constructor to find instances of
6876// args[1]: the the maximum number of objects to return
6877static Object* Runtime_DebugConstructedBy(Arguments args) {
6878 ASSERT(args.length() == 2);
6879
6880 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006881 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006882
6883 // Check parameters.
6884 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6885 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6886 RUNTIME_ASSERT(max_references >= 0);
6887
6888 // Get the number of referencing objects.
6889 int count;
6890 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6891
6892 // Allocate an array to hold the result.
6893 Object* object = Heap::AllocateFixedArray(count);
6894 if (object->IsFailure()) return object;
6895 FixedArray* instances = FixedArray::cast(object);
6896
6897 // Fill the referencing objects.
6898 count = DebugConstructedBy(constructor, max_references, instances, count);
6899
6900 // Return result as JS array.
6901 Object* result =
6902 Heap::AllocateJSObject(
6903 Top::context()->global_context()->array_function());
6904 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6905 return result;
6906}
6907
6908
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006909// Find the effective prototype object as returned by __proto__.
6910// args[0]: the object to find the prototype for.
6911static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912 ASSERT(args.length() == 1);
6913
6914 CONVERT_CHECKED(JSObject, obj, args[0]);
6915
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006916 // Use the __proto__ accessor.
6917 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006918}
6919
6920
6921static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006922 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006923 CPU::DebugBreak();
6924 return Heap::undefined_value();
6925}
6926
6927
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006928static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6929#ifdef DEBUG
6930 HandleScope scope;
6931 ASSERT(args.length() == 1);
6932 // Get the function and make sure it is compiled.
6933 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6934 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6935 return Failure::Exception();
6936 }
6937 func->code()->PrintLn();
6938#endif // DEBUG
6939 return Heap::undefined_value();
6940}
ager@chromium.org9085a012009-05-11 19:22:57 +00006941
6942
6943static Object* Runtime_FunctionGetInferredName(Arguments args) {
6944 NoHandleAllocation ha;
6945 ASSERT(args.length() == 1);
6946
6947 CONVERT_CHECKED(JSFunction, f, args[0]);
6948 return f->shared()->inferred_name();
6949}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006950#endif // ENABLE_DEBUGGER_SUPPORT
6951
6952
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006953// Finds the script object from the script data. NOTE: This operation uses
6954// heap traversal to find the function generated for the source position
6955// for the requested break point. For lazily compiled functions several heap
6956// traversals might be required rendering this operation as a rather slow
6957// operation. However for setting break points which is normally done through
6958// some kind of user interaction the performance is not crucial.
6959static Handle<Object> Runtime_GetScriptFromScriptName(
6960 Handle<String> script_name) {
6961 // Scan the heap for Script objects to find the script with the requested
6962 // script data.
6963 Handle<Script> script;
6964 HeapIterator iterator;
6965 while (script.is_null() && iterator.has_next()) {
6966 HeapObject* obj = iterator.next();
6967 // If a script is found check if it has the script data requested.
6968 if (obj->IsScript()) {
6969 if (Script::cast(obj)->name()->IsString()) {
6970 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6971 script = Handle<Script>(Script::cast(obj));
6972 }
6973 }
6974 }
6975 }
6976
6977 // If no script with the requested script data is found return undefined.
6978 if (script.is_null()) return Factory::undefined_value();
6979
6980 // Return the script found.
6981 return GetScriptWrapper(script);
6982}
6983
6984
6985// Get the script object from script data. NOTE: Regarding performance
6986// see the NOTE for GetScriptFromScriptData.
6987// args[0]: script data for the script to find the source for
6988static Object* Runtime_GetScript(Arguments args) {
6989 HandleScope scope;
6990
6991 ASSERT(args.length() == 1);
6992
6993 CONVERT_CHECKED(String, script_name, args[0]);
6994
6995 // Find the requested script.
6996 Handle<Object> result =
6997 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
6998 return *result;
6999}
7000
7001
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007002static Object* Runtime_Abort(Arguments args) {
7003 ASSERT(args.length() == 2);
7004 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7005 Smi::cast(args[1])->value());
7006 Top::PrintStack();
7007 OS::Abort();
7008 UNREACHABLE();
7009 return NULL;
7010}
7011
7012
kasper.lund44510672008-07-25 07:37:58 +00007013#ifdef DEBUG
7014// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7015// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007016static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007017 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007018 HandleScope scope;
7019 Handle<JSArray> result = Factory::NewJSArray(0);
7020 int index = 0;
7021#define ADD_ENTRY(Name, argc) \
7022 { \
7023 HandleScope inner; \
7024 Handle<String> name = \
7025 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7026 Handle<JSArray> pair = Factory::NewJSArray(0); \
7027 SetElement(pair, 0, name); \
7028 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7029 SetElement(result, index++, pair); \
7030 }
7031 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7032#undef ADD_ENTRY
7033 return *result;
7034}
kasper.lund44510672008-07-25 07:37:58 +00007035#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007036
7037
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007038static Object* Runtime_Log(Arguments args) {
7039 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007040 CONVERT_CHECKED(String, format, args[0]);
7041 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007042 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007043 Logger::LogRuntime(chars, elms);
7044 return Heap::undefined_value();
7045}
7046
7047
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007048static Object* Runtime_IS_VAR(Arguments args) {
7049 UNREACHABLE(); // implemented as macro in the parser
7050 return NULL;
7051}
7052
7053
7054// ----------------------------------------------------------------------------
7055// Implementation of Runtime
7056
7057#define F(name, nargs) \
7058 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7059 static_cast<int>(Runtime::k##name) },
7060
7061static Runtime::Function Runtime_functions[] = {
7062 RUNTIME_FUNCTION_LIST(F)
7063 { NULL, NULL, NULL, 0, -1 }
7064};
7065
7066#undef F
7067
7068
7069Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7070 ASSERT(0 <= fid && fid < kNofFunctions);
7071 return &Runtime_functions[fid];
7072}
7073
7074
7075Runtime::Function* Runtime::FunctionForName(const char* name) {
7076 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7077 if (strcmp(f->name, name) == 0) {
7078 return f;
7079 }
7080 }
7081 return NULL;
7082}
7083
7084
7085void Runtime::PerformGC(Object* result) {
7086 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007087 if (failure->IsRetryAfterGC()) {
7088 // Try to do a garbage collection; ignore it if it fails. The C
7089 // entry stub will throw an out-of-memory exception in that case.
7090 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7091 } else {
7092 // Handle last resort GC and make sure to allow future allocations
7093 // to grow the heap without causing GCs (if possible).
7094 Counters::gc_last_resort_from_js.Increment();
7095 Heap::CollectAllGarbage();
7096 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007097}
7098
7099
7100} } // namespace v8::internal