blob: bf6286fe34b66980f13458f098e1f5bc6defaf01 [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.
1726 ZoneScope zone(DELETE_ON_EXIT);
1727 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
2379 ZoneScope zone_space(DELETE_ON_EXIT);
2380 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
4359static Object* Runtime_NewContext(Arguments args) {
4360 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004361 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004362
kasper.lund7276f142008-07-30 08:49:36 +00004363 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004364 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4365 Object* result = Heap::AllocateFunctionContext(length, function);
4366 if (result->IsFailure()) return result;
4367
4368 Top::set_context(Context::cast(result));
4369
kasper.lund7276f142008-07-30 08:49:36 +00004370 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004371}
4372
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004373static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004374 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004375 Object* js_object = object;
4376 if (!js_object->IsJSObject()) {
4377 js_object = js_object->ToObject();
4378 if (js_object->IsFailure()) {
4379 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004380 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004381 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004382 Handle<Object> result =
4383 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4384 return Top::Throw(*result);
4385 }
4386 }
4387
4388 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004389 Heap::AllocateWithContext(Top::context(),
4390 JSObject::cast(js_object),
4391 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004392 if (result->IsFailure()) return result;
4393
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004394 Context* context = Context::cast(result);
4395 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004396
kasper.lund7276f142008-07-30 08:49:36 +00004397 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004398}
4399
4400
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004401static Object* Runtime_PushContext(Arguments args) {
4402 NoHandleAllocation ha;
4403 ASSERT(args.length() == 1);
4404 return PushContextHelper(args[0], false);
4405}
4406
4407
4408static Object* Runtime_PushCatchContext(Arguments args) {
4409 NoHandleAllocation ha;
4410 ASSERT(args.length() == 1);
4411 return PushContextHelper(args[0], true);
4412}
4413
4414
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004415static Object* Runtime_LookupContext(Arguments args) {
4416 HandleScope scope;
4417 ASSERT(args.length() == 2);
4418
4419 CONVERT_ARG_CHECKED(Context, context, 0);
4420 CONVERT_ARG_CHECKED(String, name, 1);
4421
4422 int index;
4423 PropertyAttributes attributes;
4424 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004425 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004426 context->Lookup(name, flags, &index, &attributes);
4427
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004428 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004429 ASSERT(holder->IsJSObject());
4430 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004431 }
4432
4433 // No intermediate context found. Use global object by default.
4434 return Top::context()->global();
4435}
4436
4437
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004438// A mechanism to return pairs of Object*'s. This is somewhat
4439// compiler-dependent as it assumes that a 64-bit value (a long long)
4440// is returned via two registers (edx:eax on ia32). Both the ia32 and
4441// arm platform support this; it is mostly an issue of "coaxing" the
4442// compiler to do the right thing.
4443//
4444// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004445typedef uint64_t ObjectPair;
4446static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004447 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004448 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004449}
4450
4451
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004452static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004453 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4454 USE(attributes);
4455 return x->IsTheHole() ? Heap::undefined_value() : x;
4456}
4457
4458
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004459static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4460 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004461 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004462 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004463 JSFunction* context_extension_function =
4464 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004465 // If the holder isn't a context extension object, we just return it
4466 // as the receiver. This allows arguments objects to be used as
4467 // receivers, but only if they are put in the context scope chain
4468 // explicitly via a with-statement.
4469 Object* constructor = holder->map()->constructor();
4470 if (constructor != context_extension_function) return holder;
4471 // Fall back to using the global object as the receiver if the
4472 // property turns out to be a local variable allocated in a context
4473 // extension object - introduced via eval.
4474 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004475}
4476
4477
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004478static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004479 HandleScope scope;
4480 ASSERT(args.length() == 2);
4481
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004482 if (!args[0]->IsContext() || !args[1]->IsString()) {
4483 return MakePair(IllegalOperation(), NULL);
4484 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004485 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004486 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004487
4488 int index;
4489 PropertyAttributes attributes;
4490 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004491 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004492 context->Lookup(name, flags, &index, &attributes);
4493
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004494 // If the index is non-negative, the slot has been found in a local
4495 // variable or a parameter. Read it from the context object or the
4496 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004497 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004498 // If the "property" we were looking for is a local variable or an
4499 // argument in a context, the receiver is the global object; see
4500 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4501 JSObject* receiver = Top::context()->global()->global_receiver();
4502 Object* value = (holder->IsContext())
4503 ? Context::cast(*holder)->get(index)
4504 : JSObject::cast(*holder)->GetElement(index);
4505 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004506 }
4507
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004508 // If the holder is found, we read the property from it.
4509 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004510 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004511 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004512 JSObject* receiver;
4513 if (object->IsGlobalObject()) {
4514 receiver = GlobalObject::cast(object)->global_receiver();
4515 } else if (context->is_exception_holder(*holder)) {
4516 receiver = Top::context()->global()->global_receiver();
4517 } else {
4518 receiver = ComputeReceiverForNonGlobal(object);
4519 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004520 // No need to unhole the value here. This is taken care of by the
4521 // GetProperty function.
4522 Object* value = object->GetProperty(*name);
4523 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004524 }
4525
4526 if (throw_error) {
4527 // The property doesn't exist - throw exception.
4528 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004529 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004530 return MakePair(Top::Throw(*reference_error), NULL);
4531 } else {
4532 // The property doesn't exist - return undefined
4533 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4534 }
4535}
4536
4537
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004538static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004539 return LoadContextSlotHelper(args, true);
4540}
4541
4542
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004543static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004544 return LoadContextSlotHelper(args, false);
4545}
4546
4547
4548static Object* Runtime_StoreContextSlot(Arguments args) {
4549 HandleScope scope;
4550 ASSERT(args.length() == 3);
4551
4552 Handle<Object> value(args[0]);
4553 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004554 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004555
4556 int index;
4557 PropertyAttributes attributes;
4558 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004559 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004560 context->Lookup(name, flags, &index, &attributes);
4561
4562 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004563 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004564 // Ignore if read_only variable.
4565 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004566 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004567 }
4568 } else {
4569 ASSERT((attributes & READ_ONLY) == 0);
4570 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004571 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004572 USE(result);
4573 ASSERT(!result->IsFailure());
4574 }
4575 return *value;
4576 }
4577
4578 // Slow case: The property is not in a FixedArray context.
4579 // It is either in an JSObject extension context or it was not found.
4580 Handle<JSObject> context_ext;
4581
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004582 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004583 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004584 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004585 } else {
4586 // The property was not found. It needs to be stored in the global context.
4587 ASSERT(attributes == ABSENT);
4588 attributes = NONE;
4589 context_ext = Handle<JSObject>(Top::context()->global());
4590 }
4591
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004592 // Set the property, but ignore if read_only variable on the context
4593 // extension object itself.
4594 if ((attributes & READ_ONLY) == 0 ||
4595 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004596 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4597 if (set.is_null()) {
4598 // Failure::Exception is converted to a null handle in the
4599 // handle-based methods such as SetProperty. We therefore need
4600 // to convert null handles back to exceptions.
4601 ASSERT(Top::has_pending_exception());
4602 return Failure::Exception();
4603 }
4604 }
4605 return *value;
4606}
4607
4608
4609static Object* Runtime_Throw(Arguments args) {
4610 HandleScope scope;
4611 ASSERT(args.length() == 1);
4612
4613 return Top::Throw(args[0]);
4614}
4615
4616
4617static Object* Runtime_ReThrow(Arguments args) {
4618 HandleScope scope;
4619 ASSERT(args.length() == 1);
4620
4621 return Top::ReThrow(args[0]);
4622}
4623
4624
4625static Object* Runtime_ThrowReferenceError(Arguments args) {
4626 HandleScope scope;
4627 ASSERT(args.length() == 1);
4628
4629 Handle<Object> name(args[0]);
4630 Handle<Object> reference_error =
4631 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4632 return Top::Throw(*reference_error);
4633}
4634
4635
4636static Object* Runtime_StackOverflow(Arguments args) {
4637 NoHandleAllocation na;
4638 return Top::StackOverflow();
4639}
4640
4641
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004642static Object* Runtime_StackGuard(Arguments args) {
4643 ASSERT(args.length() == 1);
4644
4645 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004646 if (StackGuard::IsStackOverflow()) {
4647 return Runtime_StackOverflow(args);
4648 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004649
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004650 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004651}
4652
4653
4654// NOTE: These PrintXXX functions are defined for all builds (not just
4655// DEBUG builds) because we may want to be able to trace function
4656// calls in all modes.
4657static void PrintString(String* str) {
4658 // not uncommon to have empty strings
4659 if (str->length() > 0) {
4660 SmartPointer<char> s =
4661 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4662 PrintF("%s", *s);
4663 }
4664}
4665
4666
4667static void PrintObject(Object* obj) {
4668 if (obj->IsSmi()) {
4669 PrintF("%d", Smi::cast(obj)->value());
4670 } else if (obj->IsString() || obj->IsSymbol()) {
4671 PrintString(String::cast(obj));
4672 } else if (obj->IsNumber()) {
4673 PrintF("%g", obj->Number());
4674 } else if (obj->IsFailure()) {
4675 PrintF("<failure>");
4676 } else if (obj->IsUndefined()) {
4677 PrintF("<undefined>");
4678 } else if (obj->IsNull()) {
4679 PrintF("<null>");
4680 } else if (obj->IsTrue()) {
4681 PrintF("<true>");
4682 } else if (obj->IsFalse()) {
4683 PrintF("<false>");
4684 } else {
4685 PrintF("%p", obj);
4686 }
4687}
4688
4689
4690static int StackSize() {
4691 int n = 0;
4692 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4693 return n;
4694}
4695
4696
4697static void PrintTransition(Object* result) {
4698 // indentation
4699 { const int nmax = 80;
4700 int n = StackSize();
4701 if (n <= nmax)
4702 PrintF("%4d:%*s", n, n, "");
4703 else
4704 PrintF("%4d:%*s", n, nmax, "...");
4705 }
4706
4707 if (result == NULL) {
4708 // constructor calls
4709 JavaScriptFrameIterator it;
4710 JavaScriptFrame* frame = it.frame();
4711 if (frame->IsConstructor()) PrintF("new ");
4712 // function name
4713 Object* fun = frame->function();
4714 if (fun->IsJSFunction()) {
4715 PrintObject(JSFunction::cast(fun)->shared()->name());
4716 } else {
4717 PrintObject(fun);
4718 }
4719 // function arguments
4720 // (we are intentionally only printing the actually
4721 // supplied parameters, not all parameters required)
4722 PrintF("(this=");
4723 PrintObject(frame->receiver());
4724 const int length = frame->GetProvidedParametersCount();
4725 for (int i = 0; i < length; i++) {
4726 PrintF(", ");
4727 PrintObject(frame->GetParameter(i));
4728 }
4729 PrintF(") {\n");
4730
4731 } else {
4732 // function result
4733 PrintF("} -> ");
4734 PrintObject(result);
4735 PrintF("\n");
4736 }
4737}
4738
4739
4740static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004741 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004742 NoHandleAllocation ha;
4743 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004744 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004745}
4746
4747
4748static Object* Runtime_TraceExit(Arguments args) {
4749 NoHandleAllocation ha;
4750 PrintTransition(args[0]);
4751 return args[0]; // return TOS
4752}
4753
4754
4755static Object* Runtime_DebugPrint(Arguments args) {
4756 NoHandleAllocation ha;
4757 ASSERT(args.length() == 1);
4758
4759#ifdef DEBUG
4760 if (args[0]->IsString()) {
4761 // If we have a string, assume it's a code "marker"
4762 // and print some interesting cpu debugging info.
4763 JavaScriptFrameIterator it;
4764 JavaScriptFrame* frame = it.frame();
4765 PrintF("fp = %p, sp = %p, pp = %p: ",
4766 frame->fp(), frame->sp(), frame->pp());
4767 } else {
4768 PrintF("DebugPrint: ");
4769 }
4770 args[0]->Print();
4771#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004772 // ShortPrint is available in release mode. Print is not.
4773 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004774#endif
4775 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004776 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777
4778 return args[0]; // return TOS
4779}
4780
4781
4782static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004783 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784 NoHandleAllocation ha;
4785 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004786 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004787}
4788
4789
mads.s.ager31e71382008-08-13 09:32:07 +00004790static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004791 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004792 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793
4794 // According to ECMA-262, section 15.9.1, page 117, the precision of
4795 // the number in a Date object representing a particular instant in
4796 // time is milliseconds. Therefore, we floor the result of getting
4797 // the OS time.
4798 double millis = floor(OS::TimeCurrentMillis());
4799 return Heap::NumberFromDouble(millis);
4800}
4801
4802
4803static Object* Runtime_DateParseString(Arguments args) {
4804 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004805 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004806
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004807 CONVERT_ARG_CHECKED(String, str, 0);
4808 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004809
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004810 CONVERT_ARG_CHECKED(JSArray, output, 1);
4811 RUNTIME_ASSERT(output->HasFastElements());
4812
4813 AssertNoAllocation no_allocation;
4814
4815 FixedArray* output_array = output->elements();
4816 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4817 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004818 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004819 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004821 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004822 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4823 }
4824
4825 if (result) {
4826 return *output;
4827 } else {
4828 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829 }
4830}
4831
4832
4833static Object* Runtime_DateLocalTimezone(Arguments args) {
4834 NoHandleAllocation ha;
4835 ASSERT(args.length() == 1);
4836
4837 CONVERT_DOUBLE_CHECKED(x, args[0]);
4838 char* zone = OS::LocalTimezone(x);
4839 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4840}
4841
4842
4843static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4844 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004845 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004846
4847 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4848}
4849
4850
4851static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4852 NoHandleAllocation ha;
4853 ASSERT(args.length() == 1);
4854
4855 CONVERT_DOUBLE_CHECKED(x, args[0]);
4856 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4857}
4858
4859
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860static Object* Runtime_NumberIsFinite(Arguments args) {
4861 NoHandleAllocation ha;
4862 ASSERT(args.length() == 1);
4863
4864 CONVERT_DOUBLE_CHECKED(value, args[0]);
4865 Object* result;
4866 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4867 result = Heap::false_value();
4868 } else {
4869 result = Heap::true_value();
4870 }
4871 return result;
4872}
4873
4874
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004875static Object* Runtime_GlobalReceiver(Arguments args) {
4876 ASSERT(args.length() == 1);
4877 Object* global = args[0];
4878 if (!global->IsJSGlobalObject()) return Heap::null_value();
4879 return JSGlobalObject::cast(global)->global_receiver();
4880}
4881
4882
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883static Object* Runtime_CompileString(Arguments args) {
4884 HandleScope scope;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004885 ASSERT_EQ(3, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004886 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00004887 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004888 CONVERT_ARG_CHECKED(Oddball, is_json, 2)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004889
ager@chromium.org381abbb2009-02-25 13:23:22 +00004890 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004891 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004892 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4893 context,
4894 line_offset->value(),
4895 true,
4896 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004897 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004898 Handle<JSFunction> fun =
4899 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4900 return *fun;
4901}
4902
4903
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004904static Handle<JSFunction> GetBuiltinFunction(String* name) {
4905 LookupResult result;
4906 Top::global_context()->builtins()->LocalLookup(name, &result);
4907 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4908}
4909
4910
4911static Object* CompileDirectEval(Handle<String> source) {
4912 // Compute the eval context.
4913 HandleScope scope;
4914 StackFrameLocator locator;
4915 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4916 Handle<Context> context(Context::cast(frame->context()));
4917 bool is_global = context->IsGlobalContext();
4918
ager@chromium.org381abbb2009-02-25 13:23:22 +00004919 // Compile source string in the current context.
4920 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004921 Compiler::CompileEval(source, context, 0, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004922 if (boilerplate.is_null()) return Failure::Exception();
4923 Handle<JSFunction> fun =
4924 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4925 return *fun;
4926}
4927
4928
4929static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4930 ASSERT(args.length() == 2);
4931
4932 HandleScope scope;
4933
4934 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4935
4936 Handle<Object> receiver;
4937
4938 // Find where the 'eval' symbol is bound. It is unaliased only if
4939 // it is bound in the global context.
4940 StackFrameLocator locator;
4941 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4942 Handle<Context> context(Context::cast(frame->context()));
4943 int index;
4944 PropertyAttributes attributes;
4945 while (!context.is_null()) {
4946 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4947 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00004948 // Stop search when eval is found or when the global context is
4949 // reached.
4950 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004951 if (context->is_function_context()) {
4952 context = Handle<Context>(Context::cast(context->closure()->context()));
4953 } else {
4954 context = Handle<Context>(context->previous());
4955 }
4956 }
4957
iposva@chromium.org245aa852009-02-10 00:49:54 +00004958 // If eval could not be resolved, it has been deleted and we need to
4959 // throw a reference error.
4960 if (attributes == ABSENT) {
4961 Handle<Object> name = Factory::eval_symbol();
4962 Handle<Object> reference_error =
4963 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4964 return Top::Throw(*reference_error);
4965 }
4966
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004967 if (context->IsGlobalContext()) {
4968 // 'eval' is bound in the global context, but it may have been overwritten.
4969 // Compare it to the builtin 'GlobalEval' function to make sure.
4970 Handle<JSFunction> global_eval =
4971 GetBuiltinFunction(Heap::global_eval_symbol());
4972 if (global_eval.is_identical_to(callee)) {
4973 // A direct eval call.
4974 if (args[1]->IsString()) {
4975 CONVERT_ARG_CHECKED(String, source, 1);
4976 // A normal eval call on a string. Compile it and return the
4977 // compiled function bound in the local context.
4978 Object* compiled_source = CompileDirectEval(source);
4979 if (compiled_source->IsFailure()) return compiled_source;
4980 receiver = Handle<Object>(frame->receiver());
4981 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4982 } else {
4983 // An eval call that is not called on a string. Global eval
4984 // deals better with this.
4985 receiver = Handle<Object>(Top::global_context()->global());
4986 }
4987 } else {
4988 // 'eval' is overwritten. Just call the function with the given arguments.
4989 receiver = Handle<Object>(Top::global_context()->global());
4990 }
4991 } else {
4992 // 'eval' is not bound in the global context. Just call the function
4993 // with the given arguments. This is not necessarily the global eval.
4994 if (receiver->IsContext()) {
4995 context = Handle<Context>::cast(receiver);
4996 receiver = Handle<Object>(context->get(index));
4997 }
4998 }
4999
5000 Handle<FixedArray> call = Factory::NewFixedArray(2);
5001 call->set(0, *callee);
5002 call->set(1, *receiver);
5003 return *call;
5004}
5005
5006
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005007static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5008 // This utility adjusts the property attributes for newly created Function
5009 // object ("new Function(...)") by changing the map.
5010 // All it does is changing the prototype property to enumerable
5011 // as specified in ECMA262, 15.3.5.2.
5012 HandleScope scope;
5013 ASSERT(args.length() == 1);
5014 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5015 ASSERT(func->map()->instance_type() ==
5016 Top::function_instance_map()->instance_type());
5017 ASSERT(func->map()->instance_size() ==
5018 Top::function_instance_map()->instance_size());
5019 func->set_map(*Top::function_instance_map());
5020 return *func;
5021}
5022
5023
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005024// Push an array unto an array of arrays if it is not already in the
5025// array. Returns true if the element was pushed on the stack and
5026// false otherwise.
5027static Object* Runtime_PushIfAbsent(Arguments args) {
5028 ASSERT(args.length() == 2);
5029 CONVERT_CHECKED(JSArray, array, args[0]);
5030 CONVERT_CHECKED(JSArray, element, args[1]);
5031 RUNTIME_ASSERT(array->HasFastElements());
5032 int length = Smi::cast(array->length())->value();
5033 FixedArray* elements = FixedArray::cast(array->elements());
5034 for (int i = 0; i < length; i++) {
5035 if (elements->get(i) == element) return Heap::false_value();
5036 }
5037 Object* obj = array->SetFastElement(length, element);
5038 if (obj->IsFailure()) return obj;
5039 return Heap::true_value();
5040}
5041
5042
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005043/**
5044 * A simple visitor visits every element of Array's.
5045 * The backend storage can be a fixed array for fast elements case,
5046 * or a dictionary for sparse array. Since Dictionary is a subtype
5047 * of FixedArray, the class can be used by both fast and slow cases.
5048 * The second parameter of the constructor, fast_elements, specifies
5049 * whether the storage is a FixedArray or Dictionary.
5050 *
5051 * An index limit is used to deal with the situation that a result array
5052 * length overflows 32-bit non-negative integer.
5053 */
5054class ArrayConcatVisitor {
5055 public:
5056 ArrayConcatVisitor(Handle<FixedArray> storage,
5057 uint32_t index_limit,
5058 bool fast_elements) :
5059 storage_(storage), index_limit_(index_limit),
5060 fast_elements_(fast_elements), index_offset_(0) { }
5061
5062 void visit(uint32_t i, Handle<Object> elm) {
5063 uint32_t index = i + index_offset_;
5064 if (index >= index_limit_) return;
5065
5066 if (fast_elements_) {
5067 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5068 storage_->set(index, *elm);
5069
5070 } else {
5071 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5072 Handle<Dictionary> result =
5073 Factory::DictionaryAtNumberPut(dict, index, elm);
5074 if (!result.is_identical_to(dict))
5075 storage_ = result;
5076 }
5077 }
5078
5079 void increase_index_offset(uint32_t delta) {
5080 index_offset_ += delta;
5081 }
5082
5083 private:
5084 Handle<FixedArray> storage_;
5085 uint32_t index_limit_;
5086 bool fast_elements_;
5087 uint32_t index_offset_;
5088};
5089
5090
5091/**
5092 * A helper function that visits elements of a JSObject. Only elements
5093 * whose index between 0 and range (exclusive) are visited.
5094 *
5095 * If the third parameter, visitor, is not NULL, the visitor is called
5096 * with parameters, 'visitor_index_offset + element index' and the element.
5097 *
5098 * It returns the number of visisted elements.
5099 */
5100static uint32_t IterateElements(Handle<JSObject> receiver,
5101 uint32_t range,
5102 ArrayConcatVisitor* visitor) {
5103 uint32_t num_of_elements = 0;
5104
5105 if (receiver->HasFastElements()) {
5106 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5107 uint32_t len = elements->length();
5108 if (range < len) len = range;
5109
5110 for (uint32_t j = 0; j < len; j++) {
5111 Handle<Object> e(elements->get(j));
5112 if (!e->IsTheHole()) {
5113 num_of_elements++;
5114 if (visitor)
5115 visitor->visit(j, e);
5116 }
5117 }
5118
5119 } else {
5120 Handle<Dictionary> dict(receiver->element_dictionary());
5121 uint32_t capacity = dict->Capacity();
5122 for (uint32_t j = 0; j < capacity; j++) {
5123 Handle<Object> k(dict->KeyAt(j));
5124 if (dict->IsKey(*k)) {
5125 ASSERT(k->IsNumber());
5126 uint32_t index = static_cast<uint32_t>(k->Number());
5127 if (index < range) {
5128 num_of_elements++;
5129 if (visitor) {
5130 visitor->visit(index,
5131 Handle<Object>(dict->ValueAt(j)));
5132 }
5133 }
5134 }
5135 }
5136 }
5137
5138 return num_of_elements;
5139}
5140
5141
5142/**
5143 * A helper function that visits elements of an Array object, and elements
5144 * on its prototypes.
5145 *
5146 * Elements on prototypes are visited first, and only elements whose indices
5147 * less than Array length are visited.
5148 *
5149 * If a ArrayConcatVisitor object is given, the visitor is called with
5150 * parameters, element's index + visitor_index_offset and the element.
5151 */
5152static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5153 ArrayConcatVisitor* visitor) {
5154 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5155 Handle<Object> obj = array;
5156
5157 static const int kEstimatedPrototypes = 3;
5158 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5159
5160 // Visit prototype first. If an element on the prototype is shadowed by
5161 // the inheritor using the same index, the ArrayConcatVisitor visits
5162 // the prototype element before the shadowing element.
5163 // The visitor can simply overwrite the old value by new value using
5164 // the same index. This follows Array::concat semantics.
5165 while (!obj->IsNull()) {
5166 objects.Add(Handle<JSObject>::cast(obj));
5167 obj = Handle<Object>(obj->GetPrototype());
5168 }
5169
5170 uint32_t nof_elements = 0;
5171 for (int i = objects.length() - 1; i >= 0; i--) {
5172 Handle<JSObject> obj = objects[i];
5173 nof_elements +=
5174 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5175 }
5176
5177 return nof_elements;
5178}
5179
5180
5181/**
5182 * A helper function of Runtime_ArrayConcat.
5183 *
5184 * The first argument is an Array of arrays and objects. It is the
5185 * same as the arguments array of Array::concat JS function.
5186 *
5187 * If an argument is an Array object, the function visits array
5188 * elements. If an argument is not an Array object, the function
5189 * visits the object as if it is an one-element array.
5190 *
5191 * If the result array index overflows 32-bit integer, the rounded
5192 * non-negative number is used as new length. For example, if one
5193 * array length is 2^32 - 1, second array length is 1, the
5194 * concatenated array length is 0.
5195 */
5196static uint32_t IterateArguments(Handle<JSArray> arguments,
5197 ArrayConcatVisitor* visitor) {
5198 uint32_t visited_elements = 0;
5199 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5200
5201 for (uint32_t i = 0; i < num_of_args; i++) {
5202 Handle<Object> obj(arguments->GetElement(i));
5203 if (obj->IsJSArray()) {
5204 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5205 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5206 uint32_t nof_elements =
5207 IterateArrayAndPrototypeElements(array, visitor);
5208 // Total elements of array and its prototype chain can be more than
5209 // the array length, but ArrayConcat can only concatenate at most
5210 // the array length number of elements.
5211 visited_elements += (nof_elements > len) ? len : nof_elements;
5212 if (visitor) visitor->increase_index_offset(len);
5213
5214 } else {
5215 if (visitor) {
5216 visitor->visit(0, obj);
5217 visitor->increase_index_offset(1);
5218 }
5219 visited_elements++;
5220 }
5221 }
5222 return visited_elements;
5223}
5224
5225
5226/**
5227 * Array::concat implementation.
5228 * See ECMAScript 262, 15.4.4.4.
5229 */
5230static Object* Runtime_ArrayConcat(Arguments args) {
5231 ASSERT(args.length() == 1);
5232 HandleScope handle_scope;
5233
5234 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5235 Handle<JSArray> arguments(arg_arrays);
5236
5237 // Pass 1: estimate the number of elements of the result
5238 // (it could be more than real numbers if prototype has elements).
5239 uint32_t result_length = 0;
5240 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5241
5242 { AssertNoAllocation nogc;
5243 for (uint32_t i = 0; i < num_of_args; i++) {
5244 Object* obj = arguments->GetElement(i);
5245 if (obj->IsJSArray()) {
5246 result_length +=
5247 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5248 } else {
5249 result_length++;
5250 }
5251 }
5252 }
5253
5254 // Allocate an empty array, will set length and content later.
5255 Handle<JSArray> result = Factory::NewJSArray(0);
5256
5257 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5258 // If estimated number of elements is more than half of length, a
5259 // fixed array (fast case) is more time and space-efficient than a
5260 // dictionary.
5261 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5262
5263 Handle<FixedArray> storage;
5264 if (fast_case) {
5265 // The backing storage array must have non-existing elements to
5266 // preserve holes across concat operations.
5267 storage = Factory::NewFixedArrayWithHoles(result_length);
5268
5269 } else {
5270 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5271 uint32_t at_least_space_for = estimate_nof_elements +
5272 (estimate_nof_elements >> 2);
5273 storage = Handle<FixedArray>::cast(
5274 Factory::NewDictionary(at_least_space_for));
5275 }
5276
5277 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5278
5279 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5280
5281 IterateArguments(arguments, &visitor);
5282
5283 result->set_length(*len);
5284 result->set_elements(*storage);
5285
5286 return *result;
5287}
5288
5289
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005290// This will not allocate (flatten the string), but it may run
5291// very slowly for very deeply nested ConsStrings. For debugging use only.
5292static Object* Runtime_GlobalPrint(Arguments args) {
5293 NoHandleAllocation ha;
5294 ASSERT(args.length() == 1);
5295
5296 CONVERT_CHECKED(String, string, args[0]);
5297 StringInputBuffer buffer(string);
5298 while (buffer.has_more()) {
5299 uint16_t character = buffer.GetNext();
5300 PrintF("%c", character);
5301 }
5302 return string;
5303}
5304
ager@chromium.org5ec48922009-05-05 07:25:34 +00005305// Moves all own elements of an object, that are below a limit, to positions
5306// starting at zero. All undefined values are placed after non-undefined values,
5307// and are followed by non-existing element. Does not change the length
5308// property.
5309// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005310static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005311 ASSERT(args.length() == 2);
5312 CONVERT_CHECKED(JSObject, object, args[0]);
5313 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5314 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005315}
5316
5317
5318// Move contents of argument 0 (an array) to argument 1 (an array)
5319static Object* Runtime_MoveArrayContents(Arguments args) {
5320 ASSERT(args.length() == 2);
5321 CONVERT_CHECKED(JSArray, from, args[0]);
5322 CONVERT_CHECKED(JSArray, to, args[1]);
5323 to->SetContent(FixedArray::cast(from->elements()));
5324 to->set_length(from->length());
5325 from->SetContent(Heap::empty_fixed_array());
5326 from->set_length(0);
5327 return to;
5328}
5329
5330
5331// How many elements does this array have?
5332static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5333 ASSERT(args.length() == 1);
5334 CONVERT_CHECKED(JSArray, array, args[0]);
5335 HeapObject* elements = array->elements();
5336 if (elements->IsDictionary()) {
5337 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5338 } else {
5339 return array->length();
5340 }
5341}
5342
5343
5344// Returns an array that tells you where in the [0, length) interval an array
5345// might have elements. Can either return keys or intervals. Keys can have
5346// gaps in (undefined). Intervals can also span over some undefined keys.
5347static Object* Runtime_GetArrayKeys(Arguments args) {
5348 ASSERT(args.length() == 2);
5349 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005350 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005351 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005352 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005353 // Create an array and get all the keys into it, then remove all the
5354 // keys that are not integers in the range 0 to length-1.
5355 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5356 int keys_length = keys->length();
5357 for (int i = 0; i < keys_length; i++) {
5358 Object* key = keys->get(i);
5359 uint32_t index;
5360 if (!Array::IndexFromObject(key, &index) || index >= length) {
5361 // Zap invalid keys.
5362 keys->set_undefined(i);
5363 }
5364 }
5365 return *Factory::NewJSArrayWithElements(keys);
5366 } else {
5367 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5368 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005369 single_interval->set(0,
5370 Smi::FromInt(-1),
5371 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005372 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5373 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005374 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005375 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005376 single_interval->set(1, *length_object);
5377 return *Factory::NewJSArrayWithElements(single_interval);
5378 }
5379}
5380
5381
5382// DefineAccessor takes an optional final argument which is the
5383// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5384// to the way accessors are implemented, it is set for both the getter
5385// and setter on the first call to DefineAccessor and ignored on
5386// subsequent calls.
5387static Object* Runtime_DefineAccessor(Arguments args) {
5388 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5389 // Compute attributes.
5390 PropertyAttributes attributes = NONE;
5391 if (args.length() == 5) {
5392 CONVERT_CHECKED(Smi, attrs, args[4]);
5393 int value = attrs->value();
5394 // Only attribute bits should be set.
5395 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5396 attributes = static_cast<PropertyAttributes>(value);
5397 }
5398
5399 CONVERT_CHECKED(JSObject, obj, args[0]);
5400 CONVERT_CHECKED(String, name, args[1]);
5401 CONVERT_CHECKED(Smi, flag, args[2]);
5402 CONVERT_CHECKED(JSFunction, fun, args[3]);
5403 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5404}
5405
5406
5407static Object* Runtime_LookupAccessor(Arguments args) {
5408 ASSERT(args.length() == 3);
5409 CONVERT_CHECKED(JSObject, obj, args[0]);
5410 CONVERT_CHECKED(String, name, args[1]);
5411 CONVERT_CHECKED(Smi, flag, args[2]);
5412 return obj->LookupAccessor(name, flag->value() == 0);
5413}
5414
5415
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005416#ifdef ENABLE_DEBUGGER_SUPPORT
5417static Object* Runtime_DebugBreak(Arguments args) {
5418 ASSERT(args.length() == 0);
5419 return Execution::DebugBreakHelper();
5420}
5421
5422
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005423// Helper functions for wrapping and unwrapping stack frame ids.
5424static Smi* WrapFrameId(StackFrame::Id id) {
5425 ASSERT(IsAligned(OffsetFrom(id), 4));
5426 return Smi::FromInt(id >> 2);
5427}
5428
5429
5430static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5431 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5432}
5433
5434
5435// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005436// args[0]: debug event listener function to set or null or undefined for
5437// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005438// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005439static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005440 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005441 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5442 args[0]->IsUndefined() ||
5443 args[0]->IsNull());
5444 Handle<Object> callback = args.at<Object>(0);
5445 Handle<Object> data = args.at<Object>(1);
5446 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005447
5448 return Heap::undefined_value();
5449}
5450
5451
5452static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005453 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005454 StackGuard::DebugBreak();
5455 return Heap::undefined_value();
5456}
5457
5458
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005459// Find the length of the prototype chain that is to to handled as one. If a
5460// prototype object is hidden it is to be viewed as part of the the object it
5461// is prototype for.
5462static int LocalPrototypeChainLength(JSObject* obj) {
5463 int count = 1;
5464 Object* proto = obj->GetPrototype();
5465 while (proto->IsJSObject() &&
5466 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5467 count++;
5468 proto = JSObject::cast(proto)->GetPrototype();
5469 }
5470 return count;
5471}
5472
5473
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005474static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005475 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005476 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005477 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005478 case NORMAL: {
5479 Dictionary* dict =
5480 JSObject::cast(result->holder())->property_dictionary();
5481 value = dict->ValueAt(result->GetDictionaryEntry());
5482 if (value->IsTheHole()) {
5483 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005484 }
5485 return value;
5486 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005487 case FIELD:
5488 value =
5489 JSObject::cast(
5490 result->holder())->FastPropertyAt(result->GetFieldIndex());
5491 if (value->IsTheHole()) {
5492 return Heap::undefined_value();
5493 }
5494 return value;
5495 case CONSTANT_FUNCTION:
5496 return result->GetConstantFunction();
5497 case CALLBACKS: {
5498 Object* structure = result->GetCallbackObject();
5499 if (structure->IsProxy()) {
5500 AccessorDescriptor* callback =
5501 reinterpret_cast<AccessorDescriptor*>(
5502 Proxy::cast(structure)->proxy());
5503 value = (callback->getter)(receiver, callback->data);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005504 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005505 value = Top::pending_exception();
5506 Top::clear_pending_exception();
5507 if (caught_exception != NULL) {
5508 *caught_exception = true;
5509 }
5510 }
5511 return value;
5512 } else {
5513 return Heap::undefined_value();
5514 }
5515 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005516 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005517 case MAP_TRANSITION:
5518 case CONSTANT_TRANSITION:
5519 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005520 return Heap::undefined_value();
5521 default:
5522 UNREACHABLE();
5523 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005524 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005525 return Heap::undefined_value();
5526}
5527
5528
ager@chromium.org32912102009-01-16 10:38:43 +00005529// Get debugger related details for an object property.
5530// args[0]: object holding property
5531// args[1]: name of the property
5532//
5533// The array returned contains the following information:
5534// 0: Property value
5535// 1: Property details
5536// 2: Property value is exception
5537// 3: Getter function if defined
5538// 4: Setter function if defined
5539// Items 2-4 are only filled if the property has either a getter or a setter
5540// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005541static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542 HandleScope scope;
5543
5544 ASSERT(args.length() == 2);
5545
5546 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5547 CONVERT_ARG_CHECKED(String, name, 1);
5548
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005549 // Skip the global proxy as it has no properties and always delegates to the
5550 // real global object.
5551 if (obj->IsJSGlobalProxy()) {
5552 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5553 }
5554
5555
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005556 // Check if the name is trivially convertible to an index and get the element
5557 // if so.
5558 uint32_t index;
5559 if (name->AsArrayIndex(&index)) {
5560 Handle<FixedArray> details = Factory::NewFixedArray(2);
5561 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5562 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5563 return *Factory::NewJSArrayWithElements(details);
5564 }
5565
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005566 // Find the number of objects making up this.
5567 int length = LocalPrototypeChainLength(*obj);
5568
5569 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005570 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005571 Handle<JSObject> jsproto = obj;
5572 for (int i = 0; i < length; i++) {
5573 jsproto->LocalLookup(*name, &result);
5574 if (result.IsProperty()) {
5575 break;
5576 }
5577 if (i < length - 1) {
5578 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5579 }
5580 }
5581
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005582 if (result.IsProperty()) {
ager@chromium.org32912102009-01-16 10:38:43 +00005583 bool caught_exception = false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00005584 Object* value = DebugLookupResultValue(*obj, &result,
5585 &caught_exception);
5586 if (value->IsFailure()) return value;
5587 Handle<Object> value_handle(value);
ager@chromium.org32912102009-01-16 10:38:43 +00005588 // If the callback object is a fixed array then it contains JavaScript
5589 // getter and/or setter.
5590 bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
5591 result.GetCallbackObject()->IsFixedArray();
5592 Handle<FixedArray> details =
5593 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005594 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005595 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005596 if (hasJavaScriptAccessors) {
5597 details->set(2,
5598 caught_exception ? Heap::true_value() : Heap::false_value());
5599 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5600 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5601 }
5602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005603 return *Factory::NewJSArrayWithElements(details);
5604 }
5605 return Heap::undefined_value();
5606}
5607
5608
5609static Object* Runtime_DebugGetProperty(Arguments args) {
5610 HandleScope scope;
5611
5612 ASSERT(args.length() == 2);
5613
5614 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5615 CONVERT_ARG_CHECKED(String, name, 1);
5616
5617 LookupResult result;
5618 obj->Lookup(*name, &result);
5619 if (result.IsProperty()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005620 return DebugLookupResultValue(*obj, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005621 }
5622 return Heap::undefined_value();
5623}
5624
5625
5626// Return the names of the local named properties.
5627// args[0]: object
5628static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5629 HandleScope scope;
5630 ASSERT(args.length() == 1);
5631 if (!args[0]->IsJSObject()) {
5632 return Heap::undefined_value();
5633 }
5634 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5635
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005636 // Skip the global proxy as it has no properties and always delegates to the
5637 // real global object.
5638 if (obj->IsJSGlobalProxy()) {
5639 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5640 }
5641
5642 // Find the number of objects making up this.
5643 int length = LocalPrototypeChainLength(*obj);
5644
5645 // Find the number of local properties for each of the objects.
5646 int* local_property_count = NewArray<int>(length);
5647 int total_property_count = 0;
5648 Handle<JSObject> jsproto = obj;
5649 for (int i = 0; i < length; i++) {
5650 int n;
5651 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5652 local_property_count[i] = n;
5653 total_property_count += n;
5654 if (i < length - 1) {
5655 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5656 }
5657 }
5658
5659 // Allocate an array with storage for all the property names.
5660 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5661
5662 // Get the property names.
5663 jsproto = obj;
5664 for (int i = 0; i < length; i++) {
5665 jsproto->GetLocalPropertyNames(*names,
5666 i == 0 ? 0 : local_property_count[i - 1]);
5667 if (i < length - 1) {
5668 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5669 }
5670 }
5671
5672 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005673 return *Factory::NewJSArrayWithElements(names);
5674}
5675
5676
5677// Return the names of the local indexed properties.
5678// args[0]: object
5679static Object* Runtime_DebugLocalElementNames(Arguments args) {
5680 HandleScope scope;
5681 ASSERT(args.length() == 1);
5682 if (!args[0]->IsJSObject()) {
5683 return Heap::undefined_value();
5684 }
5685 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5686
5687 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5688 Handle<FixedArray> names = Factory::NewFixedArray(n);
5689 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5690 return *Factory::NewJSArrayWithElements(names);
5691}
5692
5693
5694// Return the property type calculated from the property details.
5695// args[0]: smi with property details.
5696static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5697 ASSERT(args.length() == 1);
5698 CONVERT_CHECKED(Smi, details, args[0]);
5699 PropertyType type = PropertyDetails(details).type();
5700 return Smi::FromInt(static_cast<int>(type));
5701}
5702
5703
5704// Return the property attribute calculated from the property details.
5705// args[0]: smi with property details.
5706static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5707 ASSERT(args.length() == 1);
5708 CONVERT_CHECKED(Smi, details, args[0]);
5709 PropertyAttributes attributes = PropertyDetails(details).attributes();
5710 return Smi::FromInt(static_cast<int>(attributes));
5711}
5712
5713
5714// Return the property insertion index calculated from the property details.
5715// args[0]: smi with property details.
5716static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5717 ASSERT(args.length() == 1);
5718 CONVERT_CHECKED(Smi, details, args[0]);
5719 int index = PropertyDetails(details).index();
5720 return Smi::FromInt(index);
5721}
5722
5723
5724// Return information on whether an object has a named or indexed interceptor.
5725// args[0]: object
5726static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5727 HandleScope scope;
5728 ASSERT(args.length() == 1);
5729 if (!args[0]->IsJSObject()) {
5730 return Smi::FromInt(0);
5731 }
5732 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5733
5734 int result = 0;
5735 if (obj->HasNamedInterceptor()) result |= 2;
5736 if (obj->HasIndexedInterceptor()) result |= 1;
5737
5738 return Smi::FromInt(result);
5739}
5740
5741
5742// Return property names from named interceptor.
5743// args[0]: object
5744static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5745 HandleScope scope;
5746 ASSERT(args.length() == 1);
5747 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005748
ager@chromium.org32912102009-01-16 10:38:43 +00005749 if (obj->HasNamedInterceptor()) {
5750 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5751 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5752 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005753 return Heap::undefined_value();
5754}
5755
5756
5757// Return element names from indexed interceptor.
5758// args[0]: object
5759static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5760 HandleScope scope;
5761 ASSERT(args.length() == 1);
5762 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005763
ager@chromium.org32912102009-01-16 10:38:43 +00005764 if (obj->HasIndexedInterceptor()) {
5765 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5766 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5767 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005768 return Heap::undefined_value();
5769}
5770
5771
5772// Return property value from named interceptor.
5773// args[0]: object
5774// args[1]: property name
5775static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5776 HandleScope scope;
5777 ASSERT(args.length() == 2);
5778 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5779 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5780 CONVERT_ARG_CHECKED(String, name, 1);
5781
5782 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005783 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005784}
5785
5786
5787// Return element value from indexed interceptor.
5788// args[0]: object
5789// args[1]: index
5790static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5791 HandleScope scope;
5792 ASSERT(args.length() == 2);
5793 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5794 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5795 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5796
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005797 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798}
5799
5800
5801static Object* Runtime_CheckExecutionState(Arguments args) {
5802 ASSERT(args.length() >= 1);
5803 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005804 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005805 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005806 return Top::Throw(Heap::illegal_execution_state_symbol());
5807 }
5808
5809 return Heap::true_value();
5810}
5811
5812
5813static Object* Runtime_GetFrameCount(Arguments args) {
5814 HandleScope scope;
5815 ASSERT(args.length() == 1);
5816
5817 // Check arguments.
5818 Object* result = Runtime_CheckExecutionState(args);
5819 if (result->IsFailure()) return result;
5820
5821 // Count all frames which are relevant to debugging stack trace.
5822 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005823 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005824 if (id == StackFrame::NO_ID) {
5825 // If there is no JavaScript stack frame count is 0.
5826 return Smi::FromInt(0);
5827 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005828 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5829 return Smi::FromInt(n);
5830}
5831
5832
5833static const int kFrameDetailsFrameIdIndex = 0;
5834static const int kFrameDetailsReceiverIndex = 1;
5835static const int kFrameDetailsFunctionIndex = 2;
5836static const int kFrameDetailsArgumentCountIndex = 3;
5837static const int kFrameDetailsLocalCountIndex = 4;
5838static const int kFrameDetailsSourcePositionIndex = 5;
5839static const int kFrameDetailsConstructCallIndex = 6;
5840static const int kFrameDetailsDebuggerFrameIndex = 7;
5841static const int kFrameDetailsFirstDynamicIndex = 8;
5842
5843// Return an array with frame details
5844// args[0]: number: break id
5845// args[1]: number: frame index
5846//
5847// The array returned contains the following information:
5848// 0: Frame id
5849// 1: Receiver
5850// 2: Function
5851// 3: Argument count
5852// 4: Local count
5853// 5: Source position
5854// 6: Constructor call
5855// 7: Debugger frame
5856// Arguments name, value
5857// Locals name, value
5858static Object* Runtime_GetFrameDetails(Arguments args) {
5859 HandleScope scope;
5860 ASSERT(args.length() == 2);
5861
5862 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005863 Object* check = Runtime_CheckExecutionState(args);
5864 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005865 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5866
5867 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005868 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005869 if (id == StackFrame::NO_ID) {
5870 // If there are no JavaScript stack frames return undefined.
5871 return Heap::undefined_value();
5872 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005873 int count = 0;
5874 JavaScriptFrameIterator it(id);
5875 for (; !it.done(); it.Advance()) {
5876 if (count == index) break;
5877 count++;
5878 }
5879 if (it.done()) return Heap::undefined_value();
5880
5881 // Traverse the saved contexts chain to find the active context for the
5882 // selected frame.
5883 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005884 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005885 save = save->prev();
5886 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005887 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005888
5889 // Get the frame id.
5890 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5891
5892 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005893 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894
5895 // Check for constructor frame.
5896 bool constructor = it.frame()->IsConstructor();
5897
5898 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005899 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005900 ScopeInfo<> info(*code);
5901
5902 // Get the context.
5903 Handle<Context> context(Context::cast(it.frame()->context()));
5904
5905 // Get the locals names and values into a temporary array.
5906 //
5907 // TODO(1240907): Hide compiler-introduced stack variables
5908 // (e.g. .result)? For users of the debugger, they will probably be
5909 // confusing.
5910 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5911 for (int i = 0; i < info.NumberOfLocals(); i++) {
5912 // Name of the local.
5913 locals->set(i * 2, *info.LocalName(i));
5914
5915 // Fetch the value of the local - either from the stack or from a
5916 // heap-allocated context.
5917 if (i < info.number_of_stack_slots()) {
5918 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
5919 } else {
5920 Handle<String> name = info.LocalName(i);
5921 // Traverse the context chain to the function context as all local
5922 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005923 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005924 context = Handle<Context>(context->previous());
5925 }
5926 ASSERT(context->is_function_context());
5927 locals->set(i * 2 + 1,
5928 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
5929 NULL)));
5930 }
5931 }
5932
5933 // Now advance to the arguments adapter frame (if any). If contains all
5934 // the provided parameters and
5935
5936 // Now advance to the arguments adapter frame (if any). It contains all
5937 // the provided parameters whereas the function frame always have the number
5938 // of arguments matching the functions parameters. The rest of the
5939 // information (except for what is collected above) is the same.
5940 it.AdvanceToArgumentsFrame();
5941
5942 // Find the number of arguments to fill. At least fill the number of
5943 // parameters for the function and fill more if more parameters are provided.
5944 int argument_count = info.number_of_parameters();
5945 if (argument_count < it.frame()->GetProvidedParametersCount()) {
5946 argument_count = it.frame()->GetProvidedParametersCount();
5947 }
5948
5949 // Calculate the size of the result.
5950 int details_size = kFrameDetailsFirstDynamicIndex +
5951 2 * (argument_count + info.NumberOfLocals());
5952 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
5953
5954 // Add the frame id.
5955 details->set(kFrameDetailsFrameIdIndex, *frame_id);
5956
5957 // Add the function (same as in function frame).
5958 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
5959
5960 // Add the arguments count.
5961 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
5962
5963 // Add the locals count
5964 details->set(kFrameDetailsLocalCountIndex,
5965 Smi::FromInt(info.NumberOfLocals()));
5966
5967 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00005968 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005969 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
5970 } else {
5971 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
5972 }
5973
5974 // Add the constructor information.
5975 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
5976
5977 // Add information on whether this frame is invoked in the debugger context.
5978 details->set(kFrameDetailsDebuggerFrameIndex,
5979 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
5980
5981 // Fill the dynamic part.
5982 int details_index = kFrameDetailsFirstDynamicIndex;
5983
5984 // Add arguments name and value.
5985 for (int i = 0; i < argument_count; i++) {
5986 // Name of the argument.
5987 if (i < info.number_of_parameters()) {
5988 details->set(details_index++, *info.parameter_name(i));
5989 } else {
5990 details->set(details_index++, Heap::undefined_value());
5991 }
5992
5993 // Parameter value.
5994 if (i < it.frame()->GetProvidedParametersCount()) {
5995 details->set(details_index++, it.frame()->GetParameter(i));
5996 } else {
5997 details->set(details_index++, Heap::undefined_value());
5998 }
5999 }
6000
6001 // Add locals name and value from the temporary copy from the function frame.
6002 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6003 details->set(details_index++, locals->get(i));
6004 }
6005
6006 // Add the receiver (same as in function frame).
6007 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6008 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6009 Handle<Object> receiver(it.frame()->receiver());
6010 if (!receiver->IsJSObject()) {
6011 // If the receiver is NOT a JSObject we have hit an optimization
6012 // where a value object is not converted into a wrapped JS objects.
6013 // To hide this optimization from the debugger, we wrap the receiver
6014 // by creating correct wrapper object based on the calling frame's
6015 // global context.
6016 it.Advance();
6017 Handle<Context> calling_frames_global_context(
6018 Context::cast(Context::cast(it.frame()->context())->global_context()));
6019 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6020 }
6021 details->set(kFrameDetailsReceiverIndex, *receiver);
6022
6023 ASSERT_EQ(details_size, details_index);
6024 return *Factory::NewJSArrayWithElements(details);
6025}
6026
6027
6028static Object* Runtime_GetCFrames(Arguments args) {
6029 HandleScope scope;
6030 ASSERT(args.length() == 1);
6031 Object* result = Runtime_CheckExecutionState(args);
6032 if (result->IsFailure()) return result;
6033
6034 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006035 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6036 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006037 if (frames_count == OS::kStackWalkError) {
6038 return Heap::undefined_value();
6039 }
6040
6041 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6042 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6043 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6044 for (int i = 0; i < frames_count; i++) {
6045 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6046 frame_value->SetProperty(
6047 *address_str,
6048 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6049 NONE);
6050
6051 // Get the stack walk text for this frame.
6052 Handle<String> frame_text;
6053 if (strlen(frames[i].text) > 0) {
6054 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6055 frame_text = Factory::NewStringFromAscii(str);
6056 }
6057
6058 if (!frame_text.is_null()) {
6059 frame_value->SetProperty(*text_str, *frame_text, NONE);
6060 }
6061
6062 frames_array->set(i, *frame_value);
6063 }
6064 return *Factory::NewJSArrayWithElements(frames_array);
6065}
6066
6067
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006068static Object* Runtime_GetThreadCount(Arguments args) {
6069 HandleScope scope;
6070 ASSERT(args.length() == 1);
6071
6072 // Check arguments.
6073 Object* result = Runtime_CheckExecutionState(args);
6074 if (result->IsFailure()) return result;
6075
6076 // Count all archived V8 threads.
6077 int n = 0;
6078 for (ThreadState* thread = ThreadState::FirstInUse();
6079 thread != NULL;
6080 thread = thread->Next()) {
6081 n++;
6082 }
6083
6084 // Total number of threads is current thread and archived threads.
6085 return Smi::FromInt(n + 1);
6086}
6087
6088
6089static const int kThreadDetailsCurrentThreadIndex = 0;
6090static const int kThreadDetailsThreadIdIndex = 1;
6091static const int kThreadDetailsSize = 2;
6092
6093// Return an array with thread details
6094// args[0]: number: break id
6095// args[1]: number: thread index
6096//
6097// The array returned contains the following information:
6098// 0: Is current thread?
6099// 1: Thread id
6100static Object* Runtime_GetThreadDetails(Arguments args) {
6101 HandleScope scope;
6102 ASSERT(args.length() == 2);
6103
6104 // Check arguments.
6105 Object* check = Runtime_CheckExecutionState(args);
6106 if (check->IsFailure()) return check;
6107 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6108
6109 // Allocate array for result.
6110 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6111
6112 // Thread index 0 is current thread.
6113 if (index == 0) {
6114 // Fill the details.
6115 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6116 details->set(kThreadDetailsThreadIdIndex,
6117 Smi::FromInt(ThreadManager::CurrentId()));
6118 } else {
6119 // Find the thread with the requested index.
6120 int n = 1;
6121 ThreadState* thread = ThreadState::FirstInUse();
6122 while (index != n && thread != NULL) {
6123 thread = thread->Next();
6124 n++;
6125 }
6126 if (thread == NULL) {
6127 return Heap::undefined_value();
6128 }
6129
6130 // Fill the details.
6131 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6132 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6133 }
6134
6135 // Convert to JS array and return.
6136 return *Factory::NewJSArrayWithElements(details);
6137}
6138
6139
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006140static Object* Runtime_GetBreakLocations(Arguments args) {
6141 HandleScope scope;
6142 ASSERT(args.length() == 1);
6143
6144 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6145 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6146 // Find the number of break points
6147 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6148 if (break_locations->IsUndefined()) return Heap::undefined_value();
6149 // Return array as JS array
6150 return *Factory::NewJSArrayWithElements(
6151 Handle<FixedArray>::cast(break_locations));
6152}
6153
6154
6155// Set a break point in a function
6156// args[0]: function
6157// args[1]: number: break source position (within the function source)
6158// args[2]: number: break point object
6159static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6160 HandleScope scope;
6161 ASSERT(args.length() == 3);
6162 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6163 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6164 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6165 RUNTIME_ASSERT(source_position >= 0);
6166 Handle<Object> break_point_object_arg = args.at<Object>(2);
6167
6168 // Set break point.
6169 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6170
6171 return Heap::undefined_value();
6172}
6173
6174
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006175Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6176 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006177 // Iterate the heap looking for SharedFunctionInfo generated from the
6178 // script. The inner most SharedFunctionInfo containing the source position
6179 // for the requested break point is found.
6180 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6181 // which is found is not compiled it is compiled and the heap is iterated
6182 // again as the compilation might create inner functions from the newly
6183 // compiled function and the actual requested break point might be in one of
6184 // these functions.
6185 bool done = false;
6186 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006187 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006188 Handle<SharedFunctionInfo> target;
6189 // The current candidate for the last function in script:
6190 Handle<SharedFunctionInfo> last;
6191 while (!done) {
6192 HeapIterator iterator;
6193 while (iterator.has_next()) {
6194 HeapObject* obj = iterator.next();
6195 ASSERT(obj != NULL);
6196 if (obj->IsSharedFunctionInfo()) {
6197 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6198 if (shared->script() == *script) {
6199 // If the SharedFunctionInfo found has the requested script data and
6200 // contains the source position it is a candidate.
6201 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006202 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006203 start_position = shared->start_position();
6204 }
6205 if (start_position <= position &&
6206 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006207 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006208 // candidate this is the new candidate.
6209 if (target.is_null()) {
6210 target_start_position = start_position;
6211 target = shared;
6212 } else {
6213 if (target_start_position < start_position &&
6214 shared->end_position() < target->end_position()) {
6215 target_start_position = start_position;
6216 target = shared;
6217 }
6218 }
6219 }
6220
6221 // Keep track of the last function in the script.
6222 if (last.is_null() ||
6223 shared->end_position() > last->start_position()) {
6224 last = shared;
6225 }
6226 }
6227 }
6228 }
6229
6230 // Make sure some candidate is selected.
6231 if (target.is_null()) {
6232 if (!last.is_null()) {
6233 // Position after the last function - use last.
6234 target = last;
6235 } else {
6236 // Unable to find function - possibly script without any function.
6237 return Heap::undefined_value();
6238 }
6239 }
6240
6241 // If the candidate found is compiled we are done. NOTE: when lazy
6242 // compilation of inner functions is introduced some additional checking
6243 // needs to be done here to compile inner functions.
6244 done = target->is_compiled();
6245 if (!done) {
6246 // If the candidate is not compiled compile it to reveal any inner
6247 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006248 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006249 }
6250 }
6251
6252 return *target;
6253}
6254
6255
6256// Change the state of a break point in a script. NOTE: Regarding performance
6257// see the NOTE for GetScriptFromScriptData.
6258// args[0]: script to set break point in
6259// args[1]: number: break source position (within the script source)
6260// args[2]: number: break point object
6261static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6262 HandleScope scope;
6263 ASSERT(args.length() == 3);
6264 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6265 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6266 RUNTIME_ASSERT(source_position >= 0);
6267 Handle<Object> break_point_object_arg = args.at<Object>(2);
6268
6269 // Get the script from the script wrapper.
6270 RUNTIME_ASSERT(wrapper->value()->IsScript());
6271 Handle<Script> script(Script::cast(wrapper->value()));
6272
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006273 Object* result = Runtime::FindSharedFunctionInfoInScript(
6274 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006275 if (!result->IsUndefined()) {
6276 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6277 // Find position within function. The script position might be before the
6278 // source position of the first function.
6279 int position;
6280 if (shared->start_position() > source_position) {
6281 position = 0;
6282 } else {
6283 position = source_position - shared->start_position();
6284 }
6285 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6286 }
6287 return Heap::undefined_value();
6288}
6289
6290
6291// Clear a break point
6292// args[0]: number: break point object
6293static Object* Runtime_ClearBreakPoint(Arguments args) {
6294 HandleScope scope;
6295 ASSERT(args.length() == 1);
6296 Handle<Object> break_point_object_arg = args.at<Object>(0);
6297
6298 // Clear break point.
6299 Debug::ClearBreakPoint(break_point_object_arg);
6300
6301 return Heap::undefined_value();
6302}
6303
6304
6305// Change the state of break on exceptions
6306// args[0]: boolean indicating uncaught exceptions
6307// args[1]: boolean indicating on/off
6308static Object* Runtime_ChangeBreakOnException(Arguments args) {
6309 HandleScope scope;
6310 ASSERT(args.length() == 2);
6311 ASSERT(args[0]->IsNumber());
6312 ASSERT(args[1]->IsBoolean());
6313
6314 // Update break point state
6315 ExceptionBreakType type =
6316 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6317 bool enable = args[1]->ToBoolean()->IsTrue();
6318 Debug::ChangeBreakOnException(type, enable);
6319 return Heap::undefined_value();
6320}
6321
6322
6323// Prepare for stepping
6324// args[0]: break id for checking execution state
6325// args[1]: step action from the enumeration StepAction
6326// args[2]: number of times to perform the step
6327static Object* Runtime_PrepareStep(Arguments args) {
6328 HandleScope scope;
6329 ASSERT(args.length() == 3);
6330 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006331 Object* check = Runtime_CheckExecutionState(args);
6332 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006333 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6334 return Top::Throw(Heap::illegal_argument_symbol());
6335 }
6336
6337 // Get the step action and check validity.
6338 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6339 if (step_action != StepIn &&
6340 step_action != StepNext &&
6341 step_action != StepOut &&
6342 step_action != StepInMin &&
6343 step_action != StepMin) {
6344 return Top::Throw(Heap::illegal_argument_symbol());
6345 }
6346
6347 // Get the number of steps.
6348 int step_count = NumberToInt32(args[2]);
6349 if (step_count < 1) {
6350 return Top::Throw(Heap::illegal_argument_symbol());
6351 }
6352
6353 // Prepare step.
6354 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6355 return Heap::undefined_value();
6356}
6357
6358
6359// Clear all stepping set by PrepareStep.
6360static Object* Runtime_ClearStepping(Arguments args) {
6361 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006362 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006363 Debug::ClearStepping();
6364 return Heap::undefined_value();
6365}
6366
6367
6368// Creates a copy of the with context chain. The copy of the context chain is
6369// is linked to the function context supplied.
6370static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6371 Handle<Context> function_context) {
6372 // At the bottom of the chain. Return the function context to link to.
6373 if (context_chain->is_function_context()) {
6374 return function_context;
6375 }
6376
6377 // Recursively copy the with contexts.
6378 Handle<Context> previous(context_chain->previous());
6379 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6380 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006381 CopyWithContextChain(function_context, previous),
6382 extension,
6383 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006384}
6385
6386
6387// Helper function to find or create the arguments object for
6388// Runtime_DebugEvaluate.
6389static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6390 Handle<JSFunction> function,
6391 Handle<Code> code,
6392 const ScopeInfo<>* sinfo,
6393 Handle<Context> function_context) {
6394 // Try to find the value of 'arguments' to pass as parameter. If it is not
6395 // found (that is the debugged function does not reference 'arguments' and
6396 // does not support eval) then create an 'arguments' object.
6397 int index;
6398 if (sinfo->number_of_stack_slots() > 0) {
6399 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6400 if (index != -1) {
6401 return Handle<Object>(frame->GetExpression(index));
6402 }
6403 }
6404
6405 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6406 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6407 NULL);
6408 if (index != -1) {
6409 return Handle<Object>(function_context->get(index));
6410 }
6411 }
6412
6413 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006414 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6415 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006416 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006417 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006418 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006419 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006420 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006421 return arguments;
6422}
6423
6424
6425// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006426// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006427// extension part has all the parameters and locals of the function on the
6428// stack frame. A function which calls eval with the code to evaluate is then
6429// compiled in this context and called in this context. As this context
6430// replaces the context of the function on the stack frame a new (empty)
6431// function is created as well to be used as the closure for the context.
6432// This function and the context acts as replacements for the function on the
6433// stack frame presenting the same view of the values of parameters and
6434// local variables as if the piece of JavaScript was evaluated at the point
6435// where the function on the stack frame is currently stopped.
6436static Object* Runtime_DebugEvaluate(Arguments args) {
6437 HandleScope scope;
6438
6439 // Check the execution state and decode arguments frame and source to be
6440 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006441 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006442 Object* check_result = Runtime_CheckExecutionState(args);
6443 if (check_result->IsFailure()) return check_result;
6444 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6445 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006446 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6447
6448 // Handle the processing of break.
6449 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006450
6451 // Get the frame where the debugging is performed.
6452 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6453 JavaScriptFrameIterator it(id);
6454 JavaScriptFrame* frame = it.frame();
6455 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6456 Handle<Code> code(function->code());
6457 ScopeInfo<> sinfo(*code);
6458
6459 // Traverse the saved contexts chain to find the active context for the
6460 // selected frame.
6461 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006462 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006463 save = save->prev();
6464 }
6465 ASSERT(save != NULL);
6466 SaveContext savex;
6467 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006468
6469 // Create the (empty) function replacing the function on the stack frame for
6470 // the purpose of evaluating in the context created below. It is important
6471 // that this function does not describe any parameters and local variables
6472 // in the context. If it does then this will cause problems with the lookup
6473 // in Context::Lookup, where context slots for parameters and local variables
6474 // are looked at before the extension object.
6475 Handle<JSFunction> go_between =
6476 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6477 go_between->set_context(function->context());
6478#ifdef DEBUG
6479 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6480 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6481 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6482#endif
6483
6484 // Allocate and initialize a context extension object with all the
6485 // arguments, stack locals heap locals and extension properties of the
6486 // debugged function.
6487 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6488 // First fill all parameters to the context extension.
6489 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6490 SetProperty(context_ext,
6491 sinfo.parameter_name(i),
6492 Handle<Object>(frame->GetParameter(i)), NONE);
6493 }
6494 // Second fill all stack locals to the context extension.
6495 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6496 SetProperty(context_ext,
6497 sinfo.stack_slot_name(i),
6498 Handle<Object>(frame->GetExpression(i)), NONE);
6499 }
6500 // Third fill all context locals to the context extension.
6501 Handle<Context> frame_context(Context::cast(frame->context()));
6502 Handle<Context> function_context(frame_context->fcontext());
6503 for (int i = Context::MIN_CONTEXT_SLOTS;
6504 i < sinfo.number_of_context_slots();
6505 ++i) {
6506 int context_index =
6507 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6508 SetProperty(context_ext,
6509 sinfo.context_slot_name(i),
6510 Handle<Object>(function_context->get(context_index)), NONE);
6511 }
6512 // Finally copy any properties from the function context extension. This will
6513 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006514 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006515 !function_context->IsGlobalContext()) {
6516 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6517 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6518 for (int i = 0; i < keys->length(); i++) {
6519 // Names of variables introduced by eval are strings.
6520 ASSERT(keys->get(i)->IsString());
6521 Handle<String> key(String::cast(keys->get(i)));
6522 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6523 }
6524 }
6525
6526 // Allocate a new context for the debug evaluation and set the extension
6527 // object build.
6528 Handle<Context> context =
6529 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6530 context->set_extension(*context_ext);
6531 // Copy any with contexts present and chain them in front of this context.
6532 context = CopyWithContextChain(frame_context, context);
6533
6534 // Wrap the evaluation statement in a new function compiled in the newly
6535 // created context. The function has one parameter which has to be called
6536 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006537 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006538 // function(arguments,__source__) {return eval(__source__);}
6539 static const char* source_str =
6540 "function(arguments,__source__){return eval(__source__);}";
6541 static const int source_str_length = strlen(source_str);
6542 Handle<String> function_source =
6543 Factory::NewStringFromAscii(Vector<const char>(source_str,
6544 source_str_length));
6545 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006546 Compiler::CompileEval(function_source,
6547 context,
6548 0,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006549 context->IsGlobalContext(),
6550 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006551 if (boilerplate.is_null()) return Failure::Exception();
6552 Handle<JSFunction> compiled_function =
6553 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6554
6555 // Invoke the result of the compilation to get the evaluation function.
6556 bool has_pending_exception;
6557 Handle<Object> receiver(frame->receiver());
6558 Handle<Object> evaluation_function =
6559 Execution::Call(compiled_function, receiver, 0, NULL,
6560 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006561 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006562
6563 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6564 function_context);
6565
6566 // Invoke the evaluation function and return the result.
6567 const int argc = 2;
6568 Object** argv[argc] = { arguments.location(),
6569 Handle<Object>::cast(source).location() };
6570 Handle<Object> result =
6571 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6572 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006573 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006574 return *result;
6575}
6576
6577
6578static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6579 HandleScope scope;
6580
6581 // Check the execution state and decode arguments frame and source to be
6582 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006583 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006584 Object* check_result = Runtime_CheckExecutionState(args);
6585 if (check_result->IsFailure()) return check_result;
6586 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006587 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6588
6589 // Handle the processing of break.
6590 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006591
6592 // Enter the top context from before the debugger was invoked.
6593 SaveContext save;
6594 SaveContext* top = &save;
6595 while (top != NULL && *top->context() == *Debug::debug_context()) {
6596 top = top->prev();
6597 }
6598 if (top != NULL) {
6599 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006600 }
6601
6602 // Get the global context now set to the top context from before the
6603 // debugger was invoked.
6604 Handle<Context> context = Top::global_context();
6605
6606 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006607 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006608 Handle<JSFunction>(Compiler::CompileEval(source,
6609 context,
6610 0,
6611 true,
6612 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006613 if (boilerplate.is_null()) return Failure::Exception();
6614 Handle<JSFunction> compiled_function =
6615 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6616 context));
6617
6618 // Invoke the result of the compilation to get the evaluation function.
6619 bool has_pending_exception;
6620 Handle<Object> receiver = Top::global();
6621 Handle<Object> result =
6622 Execution::Call(compiled_function, receiver, 0, NULL,
6623 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006624 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006625 return *result;
6626}
6627
6628
ager@chromium.org41826e72009-03-30 13:30:57 +00006629// If an object given is an external string, check that the underlying
6630// resource is accessible. For other kinds of objects, always return true.
6631static bool IsExternalStringValid(Object* str) {
6632 if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) {
6633 return true;
6634 }
ager@chromium.org5ec48922009-05-05 07:25:34 +00006635 if (String::cast(str)->IsAsciiRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006636 return ExternalAsciiString::cast(str)->resource() != NULL;
ager@chromium.org5ec48922009-05-05 07:25:34 +00006637 } else if (String::cast(str)->IsTwoByteRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006638 return ExternalTwoByteString::cast(str)->resource() != NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00006639 } else {
6640 return true;
6641 }
6642}
6643
6644
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006645// Helper function used by Runtime_DebugGetLoadedScripts below.
6646static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
6647 NoHandleAllocation ha;
6648 AssertNoAllocation no_alloc;
6649
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006650 // Scan heap for Script objects.
6651 int count = 0;
6652 HeapIterator iterator;
6653 while (iterator.has_next()) {
6654 HeapObject* obj = iterator.next();
6655 ASSERT(obj != NULL);
ager@chromium.org41826e72009-03-30 13:30:57 +00006656 if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006657 if (instances != NULL && count < instances_size) {
6658 instances->set(count, obj);
6659 }
6660 count++;
6661 }
6662 }
6663
6664 return count;
6665}
6666
6667
6668static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6669 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006670 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006671
6672 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
ager@chromium.org32912102009-01-16 10:38:43 +00006673 // rid of all the cached script wrappers and the second gets rid of the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006674 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006675 Heap::CollectAllGarbage();
6676 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006677
6678 // Get the number of scripts.
6679 int count;
6680 count = DebugGetLoadedScripts(NULL, 0);
6681
6682 // Allocate an array to hold the result.
6683 Handle<FixedArray> instances = Factory::NewFixedArray(count);
6684
6685 // Fill the script objects.
6686 count = DebugGetLoadedScripts(*instances, count);
6687
6688 // Convert the script objects to proper JS objects.
6689 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006690 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6691 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6692 // because using
6693 // instances->set(i, *GetScriptWrapper(script))
6694 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6695 // already have deferenced the instances handle.
6696 Handle<JSValue> wrapper = GetScriptWrapper(script);
6697 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006698 }
6699
6700 // Return result as a JS array.
6701 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6702 Handle<JSArray>::cast(result)->SetContent(*instances);
6703 return *result;
6704}
6705
6706
6707// Helper function used by Runtime_DebugReferencedBy below.
6708static int DebugReferencedBy(JSObject* target,
6709 Object* instance_filter, int max_references,
6710 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006711 JSFunction* arguments_function) {
6712 NoHandleAllocation ha;
6713 AssertNoAllocation no_alloc;
6714
6715 // Iterate the heap.
6716 int count = 0;
6717 JSObject* last = NULL;
6718 HeapIterator iterator;
6719 while (iterator.has_next() &&
6720 (max_references == 0 || count < max_references)) {
6721 // Only look at all JSObjects.
6722 HeapObject* heap_obj = iterator.next();
6723 if (heap_obj->IsJSObject()) {
6724 // Skip context extension objects and argument arrays as these are
6725 // checked in the context of functions using them.
6726 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006727 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006728 obj->map()->constructor() == arguments_function) {
6729 continue;
6730 }
6731
6732 // Check if the JS object has a reference to the object looked for.
6733 if (obj->ReferencesObject(target)) {
6734 // Check instance filter if supplied. This is normally used to avoid
6735 // references from mirror objects (see Runtime_IsInPrototypeChain).
6736 if (!instance_filter->IsUndefined()) {
6737 Object* V = obj;
6738 while (true) {
6739 Object* prototype = V->GetPrototype();
6740 if (prototype->IsNull()) {
6741 break;
6742 }
6743 if (instance_filter == prototype) {
6744 obj = NULL; // Don't add this object.
6745 break;
6746 }
6747 V = prototype;
6748 }
6749 }
6750
6751 if (obj != NULL) {
6752 // Valid reference found add to instance array if supplied an update
6753 // count.
6754 if (instances != NULL && count < instances_size) {
6755 instances->set(count, obj);
6756 }
6757 last = obj;
6758 count++;
6759 }
6760 }
6761 }
6762 }
6763
6764 // Check for circular reference only. This can happen when the object is only
6765 // referenced from mirrors and has a circular reference in which case the
6766 // object is not really alive and would have been garbage collected if not
6767 // referenced from the mirror.
6768 if (count == 1 && last == target) {
6769 count = 0;
6770 }
6771
6772 // Return the number of referencing objects found.
6773 return count;
6774}
6775
6776
6777// Scan the heap for objects with direct references to an object
6778// args[0]: the object to find references to
6779// args[1]: constructor function for instances to exclude (Mirror)
6780// args[2]: the the maximum number of objects to return
6781static Object* Runtime_DebugReferencedBy(Arguments args) {
6782 ASSERT(args.length() == 3);
6783
6784 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006785 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006786
6787 // Check parameters.
6788 CONVERT_CHECKED(JSObject, target, args[0]);
6789 Object* instance_filter = args[1];
6790 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6791 instance_filter->IsJSObject());
6792 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6793 RUNTIME_ASSERT(max_references >= 0);
6794
6795 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006796 JSObject* arguments_boilerplate =
6797 Top::context()->global_context()->arguments_boilerplate();
6798 JSFunction* arguments_function =
6799 JSFunction::cast(arguments_boilerplate->map()->constructor());
6800
6801 // Get the number of referencing objects.
6802 int count;
6803 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006804 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006805
6806 // Allocate an array to hold the result.
6807 Object* object = Heap::AllocateFixedArray(count);
6808 if (object->IsFailure()) return object;
6809 FixedArray* instances = FixedArray::cast(object);
6810
6811 // Fill the referencing objects.
6812 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006813 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006814
6815 // Return result as JS array.
6816 Object* result =
6817 Heap::AllocateJSObject(
6818 Top::context()->global_context()->array_function());
6819 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6820 return result;
6821}
6822
6823
6824// Helper function used by Runtime_DebugConstructedBy below.
6825static int DebugConstructedBy(JSFunction* constructor, int max_references,
6826 FixedArray* instances, int instances_size) {
6827 AssertNoAllocation no_alloc;
6828
6829 // Iterate the heap.
6830 int count = 0;
6831 HeapIterator iterator;
6832 while (iterator.has_next() &&
6833 (max_references == 0 || count < max_references)) {
6834 // Only look at all JSObjects.
6835 HeapObject* heap_obj = iterator.next();
6836 if (heap_obj->IsJSObject()) {
6837 JSObject* obj = JSObject::cast(heap_obj);
6838 if (obj->map()->constructor() == constructor) {
6839 // Valid reference found add to instance array if supplied an update
6840 // count.
6841 if (instances != NULL && count < instances_size) {
6842 instances->set(count, obj);
6843 }
6844 count++;
6845 }
6846 }
6847 }
6848
6849 // Return the number of referencing objects found.
6850 return count;
6851}
6852
6853
6854// Scan the heap for objects constructed by a specific function.
6855// args[0]: the constructor to find instances of
6856// args[1]: the the maximum number of objects to return
6857static Object* Runtime_DebugConstructedBy(Arguments args) {
6858 ASSERT(args.length() == 2);
6859
6860 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006861 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006862
6863 // Check parameters.
6864 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6865 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6866 RUNTIME_ASSERT(max_references >= 0);
6867
6868 // Get the number of referencing objects.
6869 int count;
6870 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6871
6872 // Allocate an array to hold the result.
6873 Object* object = Heap::AllocateFixedArray(count);
6874 if (object->IsFailure()) return object;
6875 FixedArray* instances = FixedArray::cast(object);
6876
6877 // Fill the referencing objects.
6878 count = DebugConstructedBy(constructor, max_references, instances, count);
6879
6880 // Return result as JS array.
6881 Object* result =
6882 Heap::AllocateJSObject(
6883 Top::context()->global_context()->array_function());
6884 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6885 return result;
6886}
6887
6888
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006889// Find the effective prototype object as returned by __proto__.
6890// args[0]: the object to find the prototype for.
6891static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006892 ASSERT(args.length() == 1);
6893
6894 CONVERT_CHECKED(JSObject, obj, args[0]);
6895
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006896 // Use the __proto__ accessor.
6897 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006898}
6899
6900
6901static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006902 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006903 CPU::DebugBreak();
6904 return Heap::undefined_value();
6905}
6906
6907
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006908static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6909#ifdef DEBUG
6910 HandleScope scope;
6911 ASSERT(args.length() == 1);
6912 // Get the function and make sure it is compiled.
6913 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6914 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6915 return Failure::Exception();
6916 }
6917 func->code()->PrintLn();
6918#endif // DEBUG
6919 return Heap::undefined_value();
6920}
ager@chromium.org9085a012009-05-11 19:22:57 +00006921
6922
6923static Object* Runtime_FunctionGetInferredName(Arguments args) {
6924 NoHandleAllocation ha;
6925 ASSERT(args.length() == 1);
6926
6927 CONVERT_CHECKED(JSFunction, f, args[0]);
6928 return f->shared()->inferred_name();
6929}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006930#endif // ENABLE_DEBUGGER_SUPPORT
6931
6932
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006933// Finds the script object from the script data. NOTE: This operation uses
6934// heap traversal to find the function generated for the source position
6935// for the requested break point. For lazily compiled functions several heap
6936// traversals might be required rendering this operation as a rather slow
6937// operation. However for setting break points which is normally done through
6938// some kind of user interaction the performance is not crucial.
6939static Handle<Object> Runtime_GetScriptFromScriptName(
6940 Handle<String> script_name) {
6941 // Scan the heap for Script objects to find the script with the requested
6942 // script data.
6943 Handle<Script> script;
6944 HeapIterator iterator;
6945 while (script.is_null() && iterator.has_next()) {
6946 HeapObject* obj = iterator.next();
6947 // If a script is found check if it has the script data requested.
6948 if (obj->IsScript()) {
6949 if (Script::cast(obj)->name()->IsString()) {
6950 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6951 script = Handle<Script>(Script::cast(obj));
6952 }
6953 }
6954 }
6955 }
6956
6957 // If no script with the requested script data is found return undefined.
6958 if (script.is_null()) return Factory::undefined_value();
6959
6960 // Return the script found.
6961 return GetScriptWrapper(script);
6962}
6963
6964
6965// Get the script object from script data. NOTE: Regarding performance
6966// see the NOTE for GetScriptFromScriptData.
6967// args[0]: script data for the script to find the source for
6968static Object* Runtime_GetScript(Arguments args) {
6969 HandleScope scope;
6970
6971 ASSERT(args.length() == 1);
6972
6973 CONVERT_CHECKED(String, script_name, args[0]);
6974
6975 // Find the requested script.
6976 Handle<Object> result =
6977 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
6978 return *result;
6979}
6980
6981
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006982static Object* Runtime_Abort(Arguments args) {
6983 ASSERT(args.length() == 2);
6984 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
6985 Smi::cast(args[1])->value());
6986 Top::PrintStack();
6987 OS::Abort();
6988 UNREACHABLE();
6989 return NULL;
6990}
6991
6992
kasper.lund44510672008-07-25 07:37:58 +00006993#ifdef DEBUG
6994// ListNatives is ONLY used by the fuzz-natives.js in debug mode
6995// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006996static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006997 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006998 HandleScope scope;
6999 Handle<JSArray> result = Factory::NewJSArray(0);
7000 int index = 0;
7001#define ADD_ENTRY(Name, argc) \
7002 { \
7003 HandleScope inner; \
7004 Handle<String> name = \
7005 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7006 Handle<JSArray> pair = Factory::NewJSArray(0); \
7007 SetElement(pair, 0, name); \
7008 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7009 SetElement(result, index++, pair); \
7010 }
7011 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7012#undef ADD_ENTRY
7013 return *result;
7014}
kasper.lund44510672008-07-25 07:37:58 +00007015#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007016
7017
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007018static Object* Runtime_Log(Arguments args) {
7019 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007020 CONVERT_CHECKED(String, format, args[0]);
7021 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007022 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007023 Logger::LogRuntime(chars, elms);
7024 return Heap::undefined_value();
7025}
7026
7027
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007028static Object* Runtime_IS_VAR(Arguments args) {
7029 UNREACHABLE(); // implemented as macro in the parser
7030 return NULL;
7031}
7032
7033
7034// ----------------------------------------------------------------------------
7035// Implementation of Runtime
7036
7037#define F(name, nargs) \
7038 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7039 static_cast<int>(Runtime::k##name) },
7040
7041static Runtime::Function Runtime_functions[] = {
7042 RUNTIME_FUNCTION_LIST(F)
7043 { NULL, NULL, NULL, 0, -1 }
7044};
7045
7046#undef F
7047
7048
7049Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7050 ASSERT(0 <= fid && fid < kNofFunctions);
7051 return &Runtime_functions[fid];
7052}
7053
7054
7055Runtime::Function* Runtime::FunctionForName(const char* name) {
7056 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7057 if (strcmp(f->name, name) == 0) {
7058 return f;
7059 }
7060 }
7061 return NULL;
7062}
7063
7064
7065void Runtime::PerformGC(Object* result) {
7066 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007067 if (failure->IsRetryAfterGC()) {
7068 // Try to do a garbage collection; ignore it if it fails. The C
7069 // entry stub will throw an out-of-memory exception in that case.
7070 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7071 } else {
7072 // Handle last resort GC and make sure to allow future allocations
7073 // to grow the heap without causing GCs (if possible).
7074 Counters::gc_last_resort_from_js.Increment();
7075 Heap::CollectAllGarbage();
7076 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007077}
7078
7079
7080} } // namespace v8::internal