blob: 2fcdff1560b098b5ba51782ba8133e45dfd00af2 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
35#include "compiler.h"
36#include "cpu.h"
37#include "dateparser.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
42#include "platform.h"
43#include "runtime.h"
44#include "scopeinfo.h"
45#include "v8threads.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000046#include "smart-pointer.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000047#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048
kasperl@chromium.org71affb52009-05-26 05:44:31 +000049namespace v8 {
50namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051
52
53#define RUNTIME_ASSERT(value) do { \
54 if (!(value)) return IllegalOperation(); \
55} while (false)
56
57// Cast the given object to a value of the specified type and store
58// it in a variable with the given name. If the object is not of the
59// expected type call IllegalOperation and return.
60#define CONVERT_CHECKED(Type, name, obj) \
61 RUNTIME_ASSERT(obj->Is##Type()); \
62 Type* name = Type::cast(obj);
63
64#define CONVERT_ARG_CHECKED(Type, name, index) \
65 RUNTIME_ASSERT(args[index]->Is##Type()); \
66 Handle<Type> name = args.at<Type>(index);
67
kasper.lundbd3ec4e2008-07-09 11:06:54 +000068// Cast the given object to a boolean and store it in a variable with
69// the given name. If the object is not a boolean call IllegalOperation
70// and return.
71#define CONVERT_BOOLEAN_CHECKED(name, obj) \
72 RUNTIME_ASSERT(obj->IsBoolean()); \
73 bool name = (obj)->IsTrue();
74
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000075// Cast the given object to a Smi and store its value in an int variable
76// with the given name. If the object is not a Smi call IllegalOperation
77// and return.
78#define CONVERT_SMI_CHECKED(name, obj) \
79 RUNTIME_ASSERT(obj->IsSmi()); \
80 int name = Smi::cast(obj)->value();
81
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000082// Cast the given object to a double and store it in a variable with
83// the given name. If the object is not a number (as opposed to
84// the number not-a-number) call IllegalOperation and return.
85#define CONVERT_DOUBLE_CHECKED(name, obj) \
86 RUNTIME_ASSERT(obj->IsNumber()); \
87 double name = (obj)->Number();
88
89// Call the specified converter on the object *comand store the result in
90// a variable of the specified type with the given name. If the
91// object is not a Number call IllegalOperation and return.
92#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
93 RUNTIME_ASSERT(obj->IsNumber()); \
94 type name = NumberTo##Type(obj);
95
96// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000097static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098
99
100static Object* IllegalOperation() {
101 return Top::Throw(Heap::illegal_access_symbol());
102}
103
104
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000105static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
106 StackLimitCheck check;
107 if (check.HasOverflowed()) return Top::StackOverflow();
108
109 Object* result = Heap::CopyJSObject(boilerplate);
110 if (result->IsFailure()) return result;
111 JSObject* copy = JSObject::cast(result);
112
113 // Deep copy local properties.
114 if (copy->HasFastProperties()) {
115 FixedArray* properties = copy->properties();
116 WriteBarrierMode mode = properties->GetWriteBarrierMode();
117 for (int i = 0; i < properties->length(); i++) {
118 Object* value = properties->get(i);
119 if (value->IsJSObject()) {
120 JSObject* jsObject = JSObject::cast(value);
121 result = DeepCopyBoilerplate(jsObject);
122 if (result->IsFailure()) return result;
123 properties->set(i, result, mode);
124 }
125 }
126 mode = copy->GetWriteBarrierMode();
127 for (int i = 0; i < copy->map()->inobject_properties(); i++) {
128 Object* value = copy->InObjectPropertyAt(i);
129 if (value->IsJSObject()) {
130 JSObject* jsObject = JSObject::cast(value);
131 result = DeepCopyBoilerplate(jsObject);
132 if (result->IsFailure()) return result;
133 copy->InObjectPropertyAtPut(i, result, mode);
134 }
135 }
136 } else {
137 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
138 if (result->IsFailure()) return result;
139 FixedArray* names = FixedArray::cast(result);
140 copy->GetLocalPropertyNames(names, 0);
141 for (int i = 0; i < names->length(); i++) {
142 ASSERT(names->get(i)->IsString());
143 String* keyString = String::cast(names->get(i));
144 PropertyAttributes attributes =
145 copy->GetLocalPropertyAttribute(keyString);
146 // Only deep copy fields from the object literal expression.
147 // In particular, don't try to copy the length attribute of
148 // an array.
149 if (attributes != NONE) continue;
150 Object* value = copy->GetProperty(keyString, &attributes);
151 ASSERT(!value->IsFailure());
152 if (value->IsJSObject()) {
153 JSObject* jsObject = JSObject::cast(value);
154 result = DeepCopyBoilerplate(jsObject);
155 if (result->IsFailure()) return result;
156 result = copy->SetProperty(keyString, result, NONE);
157 if (result->IsFailure()) return result;
158 }
159 }
160 }
161
162 // Deep copy local elements.
163 if (copy->HasFastElements()) {
164 FixedArray* elements = copy->elements();
165 WriteBarrierMode mode = elements->GetWriteBarrierMode();
166 for (int i = 0; i < elements->length(); i++) {
167 Object* value = elements->get(i);
168 if (value->IsJSObject()) {
169 JSObject* jsObject = JSObject::cast(value);
170 result = DeepCopyBoilerplate(jsObject);
171 if (result->IsFailure()) return result;
172 elements->set(i, result, mode);
173 }
174 }
175 } else {
176 Dictionary* element_dictionary = copy->element_dictionary();
177 int capacity = element_dictionary->Capacity();
178 for (int i = 0; i < capacity; i++) {
179 Object* k = element_dictionary->KeyAt(i);
180 if (element_dictionary->IsKey(k)) {
181 Object* value = element_dictionary->ValueAt(i);
182 if (value->IsJSObject()) {
183 JSObject* jsObject = JSObject::cast(value);
184 result = DeepCopyBoilerplate(jsObject);
185 if (result->IsFailure()) return result;
186 element_dictionary->ValueAtPut(i, result);
187 }
188 }
189 }
190 }
191 return copy;
192}
193
194
195static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
196 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
197 return DeepCopyBoilerplate(boilerplate);
198}
199
200
201static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000202 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000203 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204}
205
206
ager@chromium.org236ad962008-09-25 09:45:57 +0000207static Handle<Map> ComputeObjectLiteralMap(
208 Handle<Context> context,
209 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000210 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000211 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000212 if (FLAG_canonicalize_object_literal_maps) {
213 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000214 int number_of_symbol_keys = 0;
215 while ((number_of_symbol_keys < number_of_properties) &&
216 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
217 number_of_symbol_keys++;
218 }
219 // Based on the number of prefix symbols key we decide whether
220 // to use the map cache in the global context.
221 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000222 if ((number_of_symbol_keys == number_of_properties) &&
223 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000224 // Create the fixed array with the key.
225 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
226 for (int i = 0; i < number_of_symbol_keys; i++) {
227 keys->set(i, constant_properties->get(i*2));
228 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000229 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000230 return Factory::ObjectLiteralMapFromCache(context, keys);
231 }
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000234 return Factory::CopyMap(
235 Handle<Map>(context->object_function()->initial_map()),
236 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000237}
238
239
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000240static Handle<Object> CreateLiteralBoilerplate(
241 Handle<FixedArray> literals,
242 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244
245static Handle<Object> CreateObjectLiteralBoilerplate(
246 Handle<FixedArray> literals,
247 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000248 // Get the global context from the literals array. This is the
249 // context in which the function was created and we use the object
250 // function from this context to create the object literal. We do
251 // not use the object function from the current global context
252 // because this might be the object function from another context
253 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000254 Handle<Context> context =
255 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
256
257 bool is_result_from_cache;
258 Handle<Map> map = ComputeObjectLiteralMap(context,
259 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000260 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000261
ager@chromium.org236ad962008-09-25 09:45:57 +0000262 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000263 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000264 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000265 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
266 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000267 for (int index = 0; index < length; index +=2) {
268 Handle<Object> key(constant_properties->get(index+0));
269 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000270 if (value->IsFixedArray()) {
271 // The value contains the constant_properties of a
272 // simple object literal.
273 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
274 value = CreateLiteralBoilerplate(literals, array);
275 if (value.is_null()) return value;
276 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000277 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000278 uint32_t element_index = 0;
279 if (key->IsSymbol()) {
280 // If key is a symbol it is not an array element.
281 Handle<String> name(String::cast(*key));
282 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000283 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284 } else if (Array::IndexFromObject(*key, &element_index)) {
285 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000286 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000287 } else {
288 // Non-uint32 number.
289 ASSERT(key->IsNumber());
290 double num = key->Number();
291 char arr[100];
292 Vector<char> buffer(arr, ARRAY_SIZE(arr));
293 const char* str = DoubleToCString(num, buffer);
294 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000295 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000296 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000297 // If setting the property on the boilerplate throws an
298 // exception, the exception is converted to an empty handle in
299 // the handle based operations. In that case, we need to
300 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000301 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000302 }
303 }
304
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000305 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000306}
307
308
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000309static Handle<Object> CreateArrayLiteralBoilerplate(
310 Handle<FixedArray> literals,
311 Handle<FixedArray> elements) {
312 // Create the JSArray.
313 Handle<JSFunction> constructor(
314 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
315 Handle<Object> object = Factory::NewJSObject(constructor);
316
317 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
318
319 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
320 for (int i = 0; i < content->length(); i++) {
321 if (content->get(i)->IsFixedArray()) {
322 // The value contains the constant_properties of a
323 // simple object literal.
324 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
325 Handle<Object> result =
326 CreateLiteralBoilerplate(literals, fa);
327 if (result.is_null()) return result;
328 content->set(i, *result);
329 }
330 }
331
332 // Set the elements.
333 Handle<JSArray>::cast(object)->SetContent(*content);
334 return object;
335}
336
337
338static Handle<Object> CreateLiteralBoilerplate(
339 Handle<FixedArray> literals,
340 Handle<FixedArray> array) {
341 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
342 switch (CompileTimeValue::GetType(array)) {
343 case CompileTimeValue::OBJECT_LITERAL:
344 return CreateObjectLiteralBoilerplate(literals, elements);
345 case CompileTimeValue::ARRAY_LITERAL:
346 return CreateArrayLiteralBoilerplate(literals, elements);
347 default:
348 UNREACHABLE();
349 return Handle<Object>::null();
350 }
351}
352
353
354static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
355 HandleScope scope;
356 ASSERT(args.length() == 3);
357 // Copy the arguments.
358 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
359 CONVERT_SMI_CHECKED(literals_index, args[1]);
360 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
361
362 Handle<Object> result =
363 CreateObjectLiteralBoilerplate(literals, constant_properties);
364
365 if (result.is_null()) return Failure::Exception();
366
367 // Update the functions literal and return the boilerplate.
368 literals->set(literals_index, *result);
369
370 return *result;
371}
372
373
374static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000375 // Takes a FixedArray of elements containing the literal elements of
376 // the array literal and produces JSArray with those elements.
377 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000378 // which contains the context from which to get the Array function
379 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000380 HandleScope scope;
381 ASSERT(args.length() == 3);
382 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
383 CONVERT_SMI_CHECKED(literals_index, args[1]);
384 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000385
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000386 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
387 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000388
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000389 // Update the functions literal and return the boilerplate.
390 literals->set(literals_index, *object);
391 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000392}
393
394
ager@chromium.org32912102009-01-16 10:38:43 +0000395static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
396 ASSERT(args.length() == 2);
397 CONVERT_CHECKED(String, key, args[0]);
398 Object* value = args[1];
399 // Create a catch context extension object.
400 JSFunction* constructor =
401 Top::context()->global_context()->context_extension_function();
402 Object* object = Heap::AllocateJSObject(constructor);
403 if (object->IsFailure()) return object;
404 // Assign the exception value to the catch variable and make sure
405 // that the catch variable is DontDelete.
406 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
407 if (value->IsFailure()) return value;
408 return object;
409}
410
411
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000412static Object* Runtime_ClassOf(Arguments args) {
413 NoHandleAllocation ha;
414 ASSERT(args.length() == 1);
415 Object* obj = args[0];
416 if (!obj->IsJSObject()) return Heap::null_value();
417 return JSObject::cast(obj)->class_name();
418}
419
ager@chromium.org7c537e22008-10-16 08:43:32 +0000420
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000421static Object* Runtime_HasStringClass(Arguments args) {
422 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000423}
424
425
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000426static Object* Runtime_HasDateClass(Arguments args) {
427 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000428}
429
430
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000431static Object* Runtime_HasArrayClass(Arguments args) {
432 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
433}
434
435
436static Object* Runtime_HasFunctionClass(Arguments args) {
437 return Heap::ToBoolean(
438 args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
439}
440
441
442static Object* Runtime_HasNumberClass(Arguments args) {
443 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
444}
445
446
447static Object* Runtime_HasBooleanClass(Arguments args) {
448 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
449}
450
451
452static Object* Runtime_HasArgumentsClass(Arguments args) {
453 return Heap::ToBoolean(
454 args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
455}
456
457
458static Object* Runtime_HasRegExpClass(Arguments args) {
459 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000460}
461
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000462
463static Object* Runtime_IsInPrototypeChain(Arguments args) {
464 NoHandleAllocation ha;
465 ASSERT(args.length() == 2);
466 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
467 Object* O = args[0];
468 Object* V = args[1];
469 while (true) {
470 Object* prototype = V->GetPrototype();
471 if (prototype->IsNull()) return Heap::false_value();
472 if (O == prototype) return Heap::true_value();
473 V = prototype;
474 }
475}
476
477
ager@chromium.org9085a012009-05-11 19:22:57 +0000478// Inserts an object as the hidden prototype of another object.
479static Object* Runtime_SetHiddenPrototype(Arguments args) {
480 NoHandleAllocation ha;
481 ASSERT(args.length() == 2);
482 CONVERT_CHECKED(JSObject, jsobject, args[0]);
483 CONVERT_CHECKED(JSObject, proto, args[1]);
484
485 // Sanity checks. The old prototype (that we are replacing) could
486 // theoretically be null, but if it is not null then check that we
487 // didn't already install a hidden prototype here.
488 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
489 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
490 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
491
492 // Allocate up front before we start altering state in case we get a GC.
493 Object* map_or_failure = proto->map()->CopyDropTransitions();
494 if (map_or_failure->IsFailure()) return map_or_failure;
495 Map* new_proto_map = Map::cast(map_or_failure);
496
497 map_or_failure = jsobject->map()->CopyDropTransitions();
498 if (map_or_failure->IsFailure()) return map_or_failure;
499 Map* new_map = Map::cast(map_or_failure);
500
501 // Set proto's prototype to be the old prototype of the object.
502 new_proto_map->set_prototype(jsobject->GetPrototype());
503 proto->set_map(new_proto_map);
504 new_proto_map->set_is_hidden_prototype();
505
506 // Set the object's prototype to proto.
507 new_map->set_prototype(proto);
508 jsobject->set_map(new_map);
509
510 return Heap::undefined_value();
511}
512
513
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000514static Object* Runtime_IsConstructCall(Arguments args) {
515 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000516 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000517 JavaScriptFrameIterator it;
518 return Heap::ToBoolean(it.frame()->IsConstructor());
519}
520
521
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000522static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000523 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000524 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +0000525 CONVERT_CHECKED(JSRegExp, raw_re, args[0]);
526 Handle<JSRegExp> re(raw_re);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000527 CONVERT_CHECKED(String, raw_pattern, args[1]);
528 Handle<String> pattern(raw_pattern);
529 CONVERT_CHECKED(String, raw_flags, args[2]);
530 Handle<String> flags(raw_flags);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000531 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
532 if (result.is_null()) return Failure::Exception();
533 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000534}
535
536
537static Object* Runtime_CreateApiFunction(Arguments args) {
538 HandleScope scope;
539 ASSERT(args.length() == 1);
540 CONVERT_CHECKED(FunctionTemplateInfo, raw_data, args[0]);
541 Handle<FunctionTemplateInfo> data(raw_data);
542 return *Factory::CreateApiFunction(data);
543}
544
545
546static Object* Runtime_IsTemplate(Arguments args) {
547 ASSERT(args.length() == 1);
548 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000549 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000550 return Heap::ToBoolean(result);
551}
552
553
554static Object* Runtime_GetTemplateField(Arguments args) {
555 ASSERT(args.length() == 2);
556 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000557 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000558 int index = field->value();
559 int offset = index * kPointerSize + HeapObject::kHeaderSize;
560 InstanceType type = templ->map()->instance_type();
561 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
562 type == OBJECT_TEMPLATE_INFO_TYPE);
563 RUNTIME_ASSERT(offset > 0);
564 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
565 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
566 } else {
567 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
568 }
569 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000570}
571
572
ager@chromium.org870a0b62008-11-04 11:43:05 +0000573static Object* Runtime_DisableAccessChecks(Arguments args) {
574 ASSERT(args.length() == 1);
575 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000576 Map* old_map = object->map();
577 bool needs_access_checks = old_map->is_access_check_needed();
578 if (needs_access_checks) {
579 // Copy map so it won't interfere constructor's initial map.
580 Object* new_map = old_map->CopyDropTransitions();
581 if (new_map->IsFailure()) return new_map;
582
583 Map::cast(new_map)->set_is_access_check_needed(false);
584 object->set_map(Map::cast(new_map));
585 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000586 return needs_access_checks ? Heap::true_value() : Heap::false_value();
587}
588
589
590static Object* Runtime_EnableAccessChecks(Arguments args) {
591 ASSERT(args.length() == 1);
592 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000593 Map* old_map = object->map();
594 if (!old_map->is_access_check_needed()) {
595 // Copy map so it won't interfere constructor's initial map.
596 Object* new_map = old_map->CopyDropTransitions();
597 if (new_map->IsFailure()) return new_map;
598
599 Map::cast(new_map)->set_is_access_check_needed(true);
600 object->set_map(Map::cast(new_map));
601 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000602 return Heap::undefined_value();
603}
604
605
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000606static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
607 HandleScope scope;
608 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
609 Handle<Object> args[2] = { type_handle, name };
610 Handle<Object> error =
611 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
612 return Top::Throw(*error);
613}
614
615
616static Object* Runtime_DeclareGlobals(Arguments args) {
617 HandleScope scope;
618 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
619
620 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
621 Handle<Context> context = args.at<Context>(1);
622 bool is_eval = Smi::cast(args[2])->value() == 1;
623
624 // Compute the property attributes. According to ECMA-262, section
625 // 13, page 71, the property must be read-only and
626 // non-deletable. However, neither SpiderMonkey nor KJS creates the
627 // property as read-only, so we don't either.
628 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
629
630 // Only optimize the object if we intend to add more than 5 properties.
631 OptimizedObjectForAddingMultipleProperties ba(global, pairs->length()/2 > 5);
632
633 // Traverse the name/value pairs and set the properties.
634 int length = pairs->length();
635 for (int i = 0; i < length; i += 2) {
636 HandleScope scope;
637 Handle<String> name(String::cast(pairs->get(i)));
638 Handle<Object> value(pairs->get(i + 1));
639
640 // We have to declare a global const property. To capture we only
641 // assign to it when evaluating the assignment for "const x =
642 // <expr>" the initial value is the hole.
643 bool is_const_property = value->IsTheHole();
644
645 if (value->IsUndefined() || is_const_property) {
646 // Lookup the property in the global object, and don't set the
647 // value of the variable if the property is already there.
648 LookupResult lookup;
649 global->Lookup(*name, &lookup);
650 if (lookup.IsProperty()) {
651 // Determine if the property is local by comparing the holder
652 // against the global object. The information will be used to
653 // avoid throwing re-declaration errors when declaring
654 // variables or constants that exist in the prototype chain.
655 bool is_local = (*global == lookup.holder());
656 // Get the property attributes and determine if the property is
657 // read-only.
658 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
659 bool is_read_only = (attributes & READ_ONLY) != 0;
660 if (lookup.type() == INTERCEPTOR) {
661 // If the interceptor says the property is there, we
662 // just return undefined without overwriting the property.
663 // Otherwise, we continue to setting the property.
664 if (attributes != ABSENT) {
665 // Check if the existing property conflicts with regards to const.
666 if (is_local && (is_read_only || is_const_property)) {
667 const char* type = (is_read_only) ? "const" : "var";
668 return ThrowRedeclarationError(type, name);
669 };
670 // The property already exists without conflicting: Go to
671 // the next declaration.
672 continue;
673 }
674 // Fall-through and introduce the absent property by using
675 // SetProperty.
676 } else {
677 if (is_local && (is_read_only || is_const_property)) {
678 const char* type = (is_read_only) ? "const" : "var";
679 return ThrowRedeclarationError(type, name);
680 }
681 // The property already exists without conflicting: Go to
682 // the next declaration.
683 continue;
684 }
685 }
686 } else {
687 // Copy the function and update its context. Use it as value.
688 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
689 Handle<JSFunction> function =
690 Factory::NewFunctionFromBoilerplate(boilerplate, context);
691 value = function;
692 }
693
694 LookupResult lookup;
695 global->LocalLookup(*name, &lookup);
696
697 PropertyAttributes attributes = is_const_property
698 ? static_cast<PropertyAttributes>(base | READ_ONLY)
699 : base;
700
701 if (lookup.IsProperty()) {
702 // There's a local property that we need to overwrite because
703 // we're either declaring a function or there's an interceptor
704 // that claims the property is absent.
705
706 // Check for conflicting re-declarations. We cannot have
707 // conflicting types in case of intercepted properties because
708 // they are absent.
709 if (lookup.type() != INTERCEPTOR &&
710 (lookup.IsReadOnly() || is_const_property)) {
711 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
712 return ThrowRedeclarationError(type, name);
713 }
714 SetProperty(global, name, value, attributes);
715 } else {
716 // If a property with this name does not already exist on the
717 // global object add the property locally. We take special
718 // precautions to always add it as a local property even in case
719 // of callbacks in the prototype chain (this rules out using
720 // SetProperty). Also, we must use the handle-based version to
721 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000722 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000723 }
724 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000725
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726 return Heap::undefined_value();
727}
728
729
730static Object* Runtime_DeclareContextSlot(Arguments args) {
731 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000732 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733
ager@chromium.org7c537e22008-10-16 08:43:32 +0000734 CONVERT_ARG_CHECKED(Context, context, 0);
735 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000737 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000738 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000739 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000740
741 // Declarations are always done in the function context.
742 context = Handle<Context>(context->fcontext());
743
744 int index;
745 PropertyAttributes attributes;
746 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000747 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000748 context->Lookup(name, flags, &index, &attributes);
749
750 if (attributes != ABSENT) {
751 // The name was declared before; check for conflicting
752 // re-declarations: This is similar to the code in parser.cc in
753 // the AstBuildingParser::Declare function.
754 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
755 // Functions are not read-only.
756 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
757 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
758 return ThrowRedeclarationError(type, name);
759 }
760
761 // Initialize it if necessary.
762 if (*initial_value != NULL) {
763 if (index >= 0) {
764 // The variable or constant context slot should always be in
765 // the function context; not in any outer context nor in the
766 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000767 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000768 if (((attributes & READ_ONLY) == 0) ||
769 context->get(index)->IsTheHole()) {
770 context->set(index, *initial_value);
771 }
772 } else {
773 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000774 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775 SetProperty(context_ext, name, initial_value, mode);
776 }
777 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000778
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000779 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000780 // The property is not in the function context. It needs to be
781 // "declared" in the function context's extension context, or in the
782 // global context.
783 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000784 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000785 // The function context's extension context exists - use it.
786 context_ext = Handle<JSObject>(context->extension());
787 } else {
788 // The function context's extension context does not exists - allocate
789 // it.
790 context_ext = Factory::NewJSObject(Top::context_extension_function());
791 // And store it in the extension slot.
792 context->set_extension(*context_ext);
793 }
794 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795
ager@chromium.org7c537e22008-10-16 08:43:32 +0000796 // Declare the property by setting it to the initial value if provided,
797 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
798 // constant declarations).
799 ASSERT(!context_ext->HasLocalProperty(*name));
800 Handle<Object> value(Heap::undefined_value());
801 if (*initial_value != NULL) value = initial_value;
802 SetProperty(context_ext, name, value, mode);
803 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
804 }
805
806 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000807}
808
809
810static Object* Runtime_InitializeVarGlobal(Arguments args) {
811 NoHandleAllocation nha;
812
813 // Determine if we need to assign to the variable if it already
814 // exists (based on the number of arguments).
815 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
816 bool assign = args.length() == 2;
817
818 CONVERT_ARG_CHECKED(String, name, 0);
819 GlobalObject* global = Top::context()->global();
820
821 // According to ECMA-262, section 12.2, page 62, the property must
822 // not be deletable.
823 PropertyAttributes attributes = DONT_DELETE;
824
825 // Lookup the property locally in the global object. If it isn't
826 // there, we add the property and take special precautions to always
827 // add it as a local property even in case of callbacks in the
828 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000829 // We have IgnoreAttributesAndSetLocalProperty for this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 LookupResult lookup;
831 global->LocalLookup(*name, &lookup);
832 if (!lookup.IsProperty()) {
833 Object* value = (assign) ? args[1] : Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000834 return global->IgnoreAttributesAndSetLocalProperty(*name,
835 value,
836 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837 }
838
839 // Determine if this is a redeclaration of something read-only.
840 if (lookup.IsReadOnly()) {
841 return ThrowRedeclarationError("const", name);
842 }
843
844 // Determine if this is a redeclaration of an intercepted read-only
845 // property and figure out if the property exists at all.
846 bool found = true;
847 PropertyType type = lookup.type();
848 if (type == INTERCEPTOR) {
849 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
850 if (intercepted == ABSENT) {
851 // The interceptor claims the property isn't there. We need to
852 // make sure to introduce it.
853 found = false;
854 } else if ((intercepted & READ_ONLY) != 0) {
855 // The property is present, but read-only. Since we're trying to
856 // overwrite it with a variable declaration we must throw a
857 // re-declaration error.
858 return ThrowRedeclarationError("const", name);
859 }
860 // Restore global object from context (in case of GC).
861 global = Top::context()->global();
862 }
863
864 if (found && !assign) {
865 // The global property is there and we're not assigning any value
866 // to it. Just return.
867 return Heap::undefined_value();
868 }
869
870 // Assign the value (or undefined) to the property.
871 Object* value = (assign) ? args[1] : Heap::undefined_value();
872 return global->SetProperty(&lookup, *name, value, attributes);
873}
874
875
876static Object* Runtime_InitializeConstGlobal(Arguments args) {
877 // All constants are declared with an initial value. The name
878 // of the constant is the first argument and the initial value
879 // is the second.
880 RUNTIME_ASSERT(args.length() == 2);
881 CONVERT_ARG_CHECKED(String, name, 0);
882 Handle<Object> value = args.at<Object>(1);
883
884 // Get the current global object from top.
885 GlobalObject* global = Top::context()->global();
886
887 // According to ECMA-262, section 12.2, page 62, the property must
888 // not be deletable. Since it's a const, it must be READ_ONLY too.
889 PropertyAttributes attributes =
890 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
891
892 // Lookup the property locally in the global object. If it isn't
893 // there, we add the property and take special precautions to always
894 // add it as a local property even in case of callbacks in the
895 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000896 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897 LookupResult lookup;
898 global->LocalLookup(*name, &lookup);
899 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000900 return global->IgnoreAttributesAndSetLocalProperty(*name,
901 *value,
902 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000903 }
904
905 // Determine if this is a redeclaration of something not
906 // read-only. In case the result is hidden behind an interceptor we
907 // need to ask it for the property attributes.
908 if (!lookup.IsReadOnly()) {
909 if (lookup.type() != INTERCEPTOR) {
910 return ThrowRedeclarationError("var", name);
911 }
912
913 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
914
915 // Throw re-declaration error if the intercepted property is present
916 // but not read-only.
917 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
918 return ThrowRedeclarationError("var", name);
919 }
920
921 // Restore global object from context (in case of GC) and continue
922 // with setting the value because the property is either absent or
923 // read-only. We also have to do redo the lookup.
924 global = Top::context()->global();
925
926 // BUG 1213579: Handle the case where we have to set a read-only
927 // property through an interceptor and only do it if it's
928 // uninitialized, e.g. the hole. Nirk...
929 global->SetProperty(*name, *value, attributes);
930 return *value;
931 }
932
933 // Set the value, but only we're assigning the initial value to a
934 // constant. For now, we determine this by checking if the
935 // current value is the hole.
936 PropertyType type = lookup.type();
937 if (type == FIELD) {
938 FixedArray* properties = global->properties();
939 int index = lookup.GetFieldIndex();
940 if (properties->get(index)->IsTheHole()) {
941 properties->set(index, *value);
942 }
943 } else if (type == NORMAL) {
944 Dictionary* dictionary = global->property_dictionary();
945 int entry = lookup.GetDictionaryEntry();
946 if (dictionary->ValueAt(entry)->IsTheHole()) {
947 dictionary->ValueAtPut(entry, *value);
948 }
949 } else {
950 // Ignore re-initialization of constants that have already been
951 // assigned a function value.
952 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
953 }
954
955 // Use the set value as the result of the operation.
956 return *value;
957}
958
959
960static Object* Runtime_InitializeConstContextSlot(Arguments args) {
961 HandleScope scope;
962 ASSERT(args.length() == 3);
963
964 Handle<Object> value(args[0]);
965 ASSERT(!value->IsTheHole());
966 CONVERT_ARG_CHECKED(Context, context, 1);
967 Handle<String> name(String::cast(args[2]));
968
969 // Initializations are always done in the function context.
970 context = Handle<Context>(context->fcontext());
971
972 int index;
973 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000974 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000975 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000976 context->Lookup(name, flags, &index, &attributes);
977
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000978 // In most situations, the property introduced by the const
979 // declaration should be present in the context extension object.
980 // However, because declaration and initialization are separate, the
981 // property might have been deleted (if it was introduced by eval)
982 // before we reach the initialization point.
983 //
984 // Example:
985 //
986 // function f() { eval("delete x; const x;"); }
987 //
988 // In that case, the initialization behaves like a normal assignment
989 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000990 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000991 // Property was found in a context.
992 if (holder->IsContext()) {
993 // The holder cannot be the function context. If it is, there
994 // should have been a const redeclaration error when declaring
995 // the const property.
996 ASSERT(!holder.is_identical_to(context));
997 if ((attributes & READ_ONLY) == 0) {
998 Handle<Context>::cast(holder)->set(index, *value);
999 }
1000 } else {
1001 // The holder is an arguments object.
1002 ASSERT((attributes & READ_ONLY) == 0);
1003 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001004 }
1005 return *value;
1006 }
1007
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001008 // The property could not be found, we introduce it in the global
1009 // context.
1010 if (attributes == ABSENT) {
1011 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1012 SetProperty(global, name, value, NONE);
1013 return *value;
1014 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001015
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001016 // The property was present in a context extension object.
1017 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001018
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001019 if (*context_ext == context->extension()) {
1020 // This is the property that was introduced by the const
1021 // declaration. Set it if it hasn't been set before. NOTE: We
1022 // cannot use GetProperty() to get the current value as it
1023 // 'unholes' the value.
1024 LookupResult lookup;
1025 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1026 ASSERT(lookup.IsProperty()); // the property was declared
1027 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1028
1029 PropertyType type = lookup.type();
1030 if (type == FIELD) {
1031 FixedArray* properties = context_ext->properties();
1032 int index = lookup.GetFieldIndex();
1033 if (properties->get(index)->IsTheHole()) {
1034 properties->set(index, *value);
1035 }
1036 } else if (type == NORMAL) {
1037 Dictionary* dictionary = context_ext->property_dictionary();
1038 int entry = lookup.GetDictionaryEntry();
1039 if (dictionary->ValueAt(entry)->IsTheHole()) {
1040 dictionary->ValueAtPut(entry, *value);
1041 }
1042 } else {
1043 // We should not reach here. Any real, named property should be
1044 // either a field or a dictionary slot.
1045 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001046 }
1047 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001048 // The property was found in a different context extension object.
1049 // Set it if it is not a read-only property.
1050 if ((attributes & READ_ONLY) == 0) {
1051 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1052 // Setting a property might throw an exception. Exceptions
1053 // are converted to empty handles in handle operations. We
1054 // need to convert back to exceptions here.
1055 if (set.is_null()) {
1056 ASSERT(Top::has_pending_exception());
1057 return Failure::Exception();
1058 }
1059 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001060 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001061
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001062 return *value;
1063}
1064
1065
1066static Object* Runtime_RegExpExec(Arguments args) {
1067 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001068 ASSERT(args.length() == 4);
ager@chromium.org236ad962008-09-25 09:45:57 +00001069 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
1070 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001071 CONVERT_CHECKED(String, raw_subject, args[1]);
1072 Handle<String> subject(raw_subject);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001073 // Due to the way the JS files are constructed this must be less than the
1074 // length of a string, i.e. it is always a Smi. We check anyway for security.
1075 CONVERT_CHECKED(Smi, index, args[2]);
1076 CONVERT_CHECKED(JSArray, raw_last_match_info, args[3]);
1077 Handle<JSArray> last_match_info(raw_last_match_info);
ager@chromium.org41826e72009-03-30 13:30:57 +00001078 RUNTIME_ASSERT(last_match_info->HasFastElements());
1079 RUNTIME_ASSERT(index->value() >= 0);
1080 RUNTIME_ASSERT(index->value() <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001081 Handle<Object> result = RegExpImpl::Exec(regexp,
1082 subject,
1083 index->value(),
1084 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001085 if (result.is_null()) return Failure::Exception();
1086 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001087}
1088
1089
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001090static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1091 HandleScope scope;
1092 ASSERT(args.length() == 4);
1093 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1094 int index = Smi::cast(args[1])->value();
1095 Handle<String> pattern = args.at<String>(2);
1096 Handle<String> flags = args.at<String>(3);
1097
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001098 // Get the RegExp function from the context in the literals array.
1099 // This is the RegExp function from the context in which the
1100 // function was created. We do not use the RegExp function from the
1101 // current global context because this might be the RegExp function
1102 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001103 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001104 Handle<JSFunction>(
1105 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001106 // Compute the regular expression literal.
1107 bool has_pending_exception;
1108 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001109 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1110 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001111 if (has_pending_exception) {
1112 ASSERT(Top::has_pending_exception());
1113 return Failure::Exception();
1114 }
1115 literals->set(index, *regexp);
1116 return *regexp;
1117}
1118
1119
1120static Object* Runtime_FunctionGetName(Arguments args) {
1121 NoHandleAllocation ha;
1122 ASSERT(args.length() == 1);
1123
1124 CONVERT_CHECKED(JSFunction, f, args[0]);
1125 return f->shared()->name();
1126}
1127
1128
ager@chromium.org236ad962008-09-25 09:45:57 +00001129static Object* Runtime_FunctionSetName(Arguments args) {
1130 NoHandleAllocation ha;
1131 ASSERT(args.length() == 2);
1132
1133 CONVERT_CHECKED(JSFunction, f, args[0]);
1134 CONVERT_CHECKED(String, name, args[1]);
1135 f->shared()->set_name(name);
1136 return Heap::undefined_value();
1137}
1138
1139
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001140static Object* Runtime_FunctionGetScript(Arguments args) {
1141 HandleScope scope;
1142 ASSERT(args.length() == 1);
1143
1144 CONVERT_CHECKED(JSFunction, fun, args[0]);
1145 Handle<Object> script = Handle<Object>(fun->shared()->script());
1146 if (!script->IsScript()) return Heap::undefined_value();
1147
1148 return *GetScriptWrapper(Handle<Script>::cast(script));
1149}
1150
1151
1152static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1153 NoHandleAllocation ha;
1154 ASSERT(args.length() == 1);
1155
1156 CONVERT_CHECKED(JSFunction, f, args[0]);
1157 return f->shared()->GetSourceCode();
1158}
1159
1160
1161static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1162 NoHandleAllocation ha;
1163 ASSERT(args.length() == 1);
1164
1165 CONVERT_CHECKED(JSFunction, fun, args[0]);
1166 int pos = fun->shared()->start_position();
1167 return Smi::FromInt(pos);
1168}
1169
1170
1171static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1172 NoHandleAllocation ha;
1173 ASSERT(args.length() == 2);
1174
1175 CONVERT_CHECKED(JSFunction, fun, args[0]);
1176 CONVERT_CHECKED(String, name, args[1]);
1177 fun->SetInstanceClassName(name);
1178 return Heap::undefined_value();
1179}
1180
1181
1182static Object* Runtime_FunctionSetLength(Arguments args) {
1183 NoHandleAllocation ha;
1184 ASSERT(args.length() == 2);
1185
1186 CONVERT_CHECKED(JSFunction, fun, args[0]);
1187 CONVERT_CHECKED(Smi, length, args[1]);
1188 fun->shared()->set_length(length->value());
1189 return length;
1190}
1191
1192
1193static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001194 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001195 ASSERT(args.length() == 2);
1196
1197 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001198 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1199 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001200 return args[0]; // return TOS
1201}
1202
1203
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001204static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1205 NoHandleAllocation ha;
1206 ASSERT(args.length() == 1);
1207
1208 CONVERT_CHECKED(JSFunction, f, args[0]);
1209 // The function_data field of the shared function info is used exclusively by
1210 // the API.
1211 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1212 : Heap::false_value();
1213}
1214
1215
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001216static Object* Runtime_SetCode(Arguments args) {
1217 HandleScope scope;
1218 ASSERT(args.length() == 2);
1219
1220 CONVERT_CHECKED(JSFunction, raw_target, args[0]);
1221 Handle<JSFunction> target(raw_target);
1222 Handle<Object> code = args.at<Object>(1);
1223
1224 Handle<Context> context(target->context());
1225
1226 if (!code->IsNull()) {
1227 RUNTIME_ASSERT(code->IsJSFunction());
1228 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1229 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1230 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1231 return Failure::Exception();
1232 }
1233 // Set the code, formal parameter count, and the length of the target
1234 // function.
1235 target->set_code(fun->code());
1236 target->shared()->set_length(fun->shared()->length());
1237 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001238 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001239 // Set the source code of the target function to undefined.
1240 // SetCode is only used for built-in constructors like String,
1241 // Array, and Object, and some web code
1242 // doesn't like seeing source code for constructors.
1243 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244 context = Handle<Context>(fun->context());
1245
1246 // Make sure we get a fresh copy of the literal vector to avoid
1247 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001248 int number_of_literals = fun->NumberOfLiterals();
1249 Handle<FixedArray> literals =
1250 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001251 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001252 // Insert the object, regexp and array functions in the literals
1253 // array prefix. These are the functions that will be used when
1254 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001255 literals->set(JSFunction::kLiteralGlobalContextIndex,
1256 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001257 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001258 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001259 }
1260
1261 target->set_context(*context);
1262 return *target;
1263}
1264
1265
1266static Object* CharCodeAt(String* subject, Object* index) {
1267 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001268 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001269 // Flatten the string. If someone wants to get a char at an index
1270 // in a cons string, it is likely that more indices will be
1271 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001272 subject->TryFlattenIfNotFlat();
1273 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001274 return Heap::nan_value();
1275 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001276 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001277}
1278
1279
1280static Object* Runtime_StringCharCodeAt(Arguments args) {
1281 NoHandleAllocation ha;
1282 ASSERT(args.length() == 2);
1283
1284 CONVERT_CHECKED(String, subject, args[0]);
1285 Object* index = args[1];
1286 return CharCodeAt(subject, index);
1287}
1288
1289
1290static Object* Runtime_CharFromCode(Arguments args) {
1291 NoHandleAllocation ha;
1292 ASSERT(args.length() == 1);
1293 uint32_t code;
1294 if (Array::IndexFromObject(args[0], &code)) {
1295 if (code <= 0xffff) {
1296 return Heap::LookupSingleCharacterStringFromCode(code);
1297 }
1298 }
1299 return Heap::empty_string();
1300}
1301
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001302// Forward declarations.
1303static const int kStringBuilderConcatHelperLengthBits = 11;
1304static const int kStringBuilderConcatHelperPositionBits = 19;
1305
1306template <typename schar>
1307static inline void StringBuilderConcatHelper(String*,
1308 schar*,
1309 FixedArray*,
1310 int);
1311
1312typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1313typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1314
1315class ReplacementStringBuilder {
1316 public:
1317 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1318 : subject_(subject),
1319 parts_(Factory::NewFixedArray(estimated_part_count)),
1320 part_count_(0),
1321 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001322 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001323 // Require a non-zero initial size. Ensures that doubling the size to
1324 // extend the array will work.
1325 ASSERT(estimated_part_count > 0);
1326 }
1327
1328 void EnsureCapacity(int elements) {
1329 int length = parts_->length();
1330 int required_length = part_count_ + elements;
1331 if (length < required_length) {
1332 int new_length = length;
1333 do {
1334 new_length *= 2;
1335 } while (new_length < required_length);
1336 Handle<FixedArray> extended_array =
1337 Factory::NewFixedArray(new_length);
1338 parts_->CopyTo(0, *extended_array, 0, part_count_);
1339 parts_ = extended_array;
1340 }
1341 }
1342
1343 void AddSubjectSlice(int from, int to) {
1344 ASSERT(from >= 0);
1345 int length = to - from;
1346 ASSERT(length > 0);
1347 // Can we encode the slice in 11 bits for length and 19 bits for
1348 // start position - as used by StringBuilderConcatHelper?
1349 if (StringBuilderSubstringLength::is_valid(length) &&
1350 StringBuilderSubstringPosition::is_valid(from)) {
1351 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1352 StringBuilderSubstringPosition::encode(from);
1353 AddElement(Smi::FromInt(encoded_slice));
1354 } else {
1355 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1356 AddElement(*slice);
1357 }
1358 IncrementCharacterCount(length);
1359 }
1360
1361
1362 void AddString(Handle<String> string) {
1363 int length = string->length();
1364 ASSERT(length > 0);
1365 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001366 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001367 is_ascii_ = false;
1368 }
1369 IncrementCharacterCount(length);
1370 }
1371
1372
1373 Handle<String> ToString() {
1374 if (part_count_ == 0) {
1375 return Factory::empty_string();
1376 }
1377
1378 Handle<String> joined_string;
1379 if (is_ascii_) {
1380 joined_string = NewRawAsciiString(character_count_);
1381 AssertNoAllocation no_alloc;
1382 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1383 char* char_buffer = seq->GetChars();
1384 StringBuilderConcatHelper(*subject_,
1385 char_buffer,
1386 *parts_,
1387 part_count_);
1388 } else {
1389 // Non-ASCII.
1390 joined_string = NewRawTwoByteString(character_count_);
1391 AssertNoAllocation no_alloc;
1392 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1393 uc16* char_buffer = seq->GetChars();
1394 StringBuilderConcatHelper(*subject_,
1395 char_buffer,
1396 *parts_,
1397 part_count_);
1398 }
1399 return joined_string;
1400 }
1401
1402
1403 void IncrementCharacterCount(int by) {
1404 if (character_count_ > Smi::kMaxValue - by) {
1405 V8::FatalProcessOutOfMemory("String.replace result too large.");
1406 }
1407 character_count_ += by;
1408 }
1409
1410 private:
1411
1412 Handle<String> NewRawAsciiString(int size) {
1413 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1414 }
1415
1416
1417 Handle<String> NewRawTwoByteString(int size) {
1418 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1419 }
1420
1421
1422 void AddElement(Object* element) {
1423 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001424 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001425 parts_->set(part_count_, element);
1426 part_count_++;
1427 }
1428
1429 Handle<String> subject_;
1430 Handle<FixedArray> parts_;
1431 int part_count_;
1432 int character_count_;
1433 bool is_ascii_;
1434};
1435
1436
1437class CompiledReplacement {
1438 public:
1439 CompiledReplacement()
1440 : parts_(1), replacement_substrings_(0) {}
1441
1442 void Compile(Handle<String> replacement,
1443 int capture_count,
1444 int subject_length);
1445
1446 void Apply(ReplacementStringBuilder* builder,
1447 int match_from,
1448 int match_to,
1449 Handle<JSArray> last_match_info);
1450
1451 // Number of distinct parts of the replacement pattern.
1452 int parts() {
1453 return parts_.length();
1454 }
1455 private:
1456 enum PartType {
1457 SUBJECT_PREFIX = 1,
1458 SUBJECT_SUFFIX,
1459 SUBJECT_CAPTURE,
1460 REPLACEMENT_SUBSTRING,
1461 REPLACEMENT_STRING,
1462
1463 NUMBER_OF_PART_TYPES
1464 };
1465
1466 struct ReplacementPart {
1467 static inline ReplacementPart SubjectMatch() {
1468 return ReplacementPart(SUBJECT_CAPTURE, 0);
1469 }
1470 static inline ReplacementPart SubjectCapture(int capture_index) {
1471 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1472 }
1473 static inline ReplacementPart SubjectPrefix() {
1474 return ReplacementPart(SUBJECT_PREFIX, 0);
1475 }
1476 static inline ReplacementPart SubjectSuffix(int subject_length) {
1477 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1478 }
1479 static inline ReplacementPart ReplacementString() {
1480 return ReplacementPart(REPLACEMENT_STRING, 0);
1481 }
1482 static inline ReplacementPart ReplacementSubString(int from, int to) {
1483 ASSERT(from >= 0);
1484 ASSERT(to > from);
1485 return ReplacementPart(-from, to);
1486 }
1487
1488 // If tag <= 0 then it is the negation of a start index of a substring of
1489 // the replacement pattern, otherwise it's a value from PartType.
1490 ReplacementPart(int tag, int data)
1491 : tag(tag), data(data) {
1492 // Must be non-positive or a PartType value.
1493 ASSERT(tag < NUMBER_OF_PART_TYPES);
1494 }
1495 // Either a value of PartType or a non-positive number that is
1496 // the negation of an index into the replacement string.
1497 int tag;
1498 // The data value's interpretation depends on the value of tag:
1499 // tag == SUBJECT_PREFIX ||
1500 // tag == SUBJECT_SUFFIX: data is unused.
1501 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1502 // tag == REPLACEMENT_SUBSTRING ||
1503 // tag == REPLACEMENT_STRING: data is index into array of substrings
1504 // of the replacement string.
1505 // tag <= 0: Temporary representation of the substring of the replacement
1506 // string ranging over -tag .. data.
1507 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1508 // substring objects.
1509 int data;
1510 };
1511
1512 template<typename Char>
1513 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1514 Vector<Char> characters,
1515 int capture_count,
1516 int subject_length) {
1517 int length = characters.length();
1518 int last = 0;
1519 for (int i = 0; i < length; i++) {
1520 Char c = characters[i];
1521 if (c == '$') {
1522 int next_index = i + 1;
1523 if (next_index == length) { // No next character!
1524 break;
1525 }
1526 Char c2 = characters[next_index];
1527 switch (c2) {
1528 case '$':
1529 if (i > last) {
1530 // There is a substring before. Include the first "$".
1531 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1532 last = next_index + 1; // Continue after the second "$".
1533 } else {
1534 // Let the next substring start with the second "$".
1535 last = next_index;
1536 }
1537 i = next_index;
1538 break;
1539 case '`':
1540 if (i > last) {
1541 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1542 }
1543 parts->Add(ReplacementPart::SubjectPrefix());
1544 i = next_index;
1545 last = i + 1;
1546 break;
1547 case '\'':
1548 if (i > last) {
1549 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1550 }
1551 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1552 i = next_index;
1553 last = i + 1;
1554 break;
1555 case '&':
1556 if (i > last) {
1557 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1558 }
1559 parts->Add(ReplacementPart::SubjectMatch());
1560 i = next_index;
1561 last = i + 1;
1562 break;
1563 case '0':
1564 case '1':
1565 case '2':
1566 case '3':
1567 case '4':
1568 case '5':
1569 case '6':
1570 case '7':
1571 case '8':
1572 case '9': {
1573 int capture_ref = c2 - '0';
1574 if (capture_ref > capture_count) {
1575 i = next_index;
1576 continue;
1577 }
1578 int second_digit_index = next_index + 1;
1579 if (second_digit_index < length) {
1580 // Peek ahead to see if we have two digits.
1581 Char c3 = characters[second_digit_index];
1582 if ('0' <= c3 && c3 <= '9') { // Double digits.
1583 int double_digit_ref = capture_ref * 10 + c3 - '0';
1584 if (double_digit_ref <= capture_count) {
1585 next_index = second_digit_index;
1586 capture_ref = double_digit_ref;
1587 }
1588 }
1589 }
1590 if (capture_ref > 0) {
1591 if (i > last) {
1592 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1593 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001594 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001595 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1596 last = next_index + 1;
1597 }
1598 i = next_index;
1599 break;
1600 }
1601 default:
1602 i = next_index;
1603 break;
1604 }
1605 }
1606 }
1607 if (length > last) {
1608 if (last == 0) {
1609 parts->Add(ReplacementPart::ReplacementString());
1610 } else {
1611 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1612 }
1613 }
1614 }
1615
1616 ZoneList<ReplacementPart> parts_;
1617 ZoneList<Handle<String> > replacement_substrings_;
1618};
1619
1620
1621void CompiledReplacement::Compile(Handle<String> replacement,
1622 int capture_count,
1623 int subject_length) {
1624 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001625 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001626 AssertNoAllocation no_alloc;
1627 ParseReplacementPattern(&parts_,
1628 replacement->ToAsciiVector(),
1629 capture_count,
1630 subject_length);
1631 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001632 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001633 AssertNoAllocation no_alloc;
1634
1635 ParseReplacementPattern(&parts_,
1636 replacement->ToUC16Vector(),
1637 capture_count,
1638 subject_length);
1639 }
1640 // Find substrings of replacement string and create them as String objects..
1641 int substring_index = 0;
1642 for (int i = 0, n = parts_.length(); i < n; i++) {
1643 int tag = parts_[i].tag;
1644 if (tag <= 0) { // A replacement string slice.
1645 int from = -tag;
1646 int to = parts_[i].data;
1647 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1648 from,
1649 to));
1650 parts_[i].tag = REPLACEMENT_SUBSTRING;
1651 parts_[i].data = substring_index;
1652 substring_index++;
1653 } else if (tag == REPLACEMENT_STRING) {
1654 replacement_substrings_.Add(replacement);
1655 parts_[i].data = substring_index;
1656 substring_index++;
1657 }
1658 }
1659}
1660
1661
1662void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1663 int match_from,
1664 int match_to,
1665 Handle<JSArray> last_match_info) {
1666 for (int i = 0, n = parts_.length(); i < n; i++) {
1667 ReplacementPart part = parts_[i];
1668 switch (part.tag) {
1669 case SUBJECT_PREFIX:
1670 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1671 break;
1672 case SUBJECT_SUFFIX: {
1673 int subject_length = part.data;
1674 if (match_to < subject_length) {
1675 builder->AddSubjectSlice(match_to, subject_length);
1676 }
1677 break;
1678 }
1679 case SUBJECT_CAPTURE: {
1680 int capture = part.data;
1681 FixedArray* match_info = last_match_info->elements();
1682 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1683 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1684 if (from >= 0 && to > from) {
1685 builder->AddSubjectSlice(from, to);
1686 }
1687 break;
1688 }
1689 case REPLACEMENT_SUBSTRING:
1690 case REPLACEMENT_STRING:
1691 builder->AddString(replacement_substrings_[part.data]);
1692 break;
1693 default:
1694 UNREACHABLE();
1695 }
1696 }
1697}
1698
1699
1700
1701static Object* StringReplaceRegExpWithString(String* subject,
1702 JSRegExp* regexp,
1703 String* replacement,
1704 JSArray* last_match_info) {
1705 ASSERT(subject->IsFlat());
1706 ASSERT(replacement->IsFlat());
1707
1708 HandleScope handles;
1709
1710 int length = subject->length();
1711 Handle<String> subject_handle(subject);
1712 Handle<JSRegExp> regexp_handle(regexp);
1713 Handle<String> replacement_handle(replacement);
1714 Handle<JSArray> last_match_info_handle(last_match_info);
1715 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1716 subject_handle,
1717 0,
1718 last_match_info_handle);
1719 if (match.is_null()) {
1720 return Failure::Exception();
1721 }
1722 if (match->IsNull()) {
1723 return *subject_handle;
1724 }
1725
1726 int capture_count = regexp_handle->CaptureCount();
1727
1728 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001729 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001730 CompiledReplacement compiled_replacement;
1731 compiled_replacement.Compile(replacement_handle,
1732 capture_count,
1733 length);
1734
1735 bool is_global = regexp_handle->GetFlags().is_global();
1736
1737 // Guessing the number of parts that the final result string is built
1738 // from. Global regexps can match any number of times, so we guess
1739 // conservatively.
1740 int expected_parts =
1741 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1742 ReplacementStringBuilder builder(subject_handle, expected_parts);
1743
1744 // Index of end of last match.
1745 int prev = 0;
1746
1747 // Number of parts added by compiled replacement plus preceeding string
1748 // and possibly suffix after last match.
1749 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1750 bool matched = true;
1751 do {
1752 ASSERT(last_match_info_handle->HasFastElements());
1753 // Increase the capacity of the builder before entering local handle-scope,
1754 // so its internal buffer can safely allocate a new handle if it grows.
1755 builder.EnsureCapacity(parts_added_per_loop);
1756
1757 HandleScope loop_scope;
1758 int start, end;
1759 {
1760 AssertNoAllocation match_info_array_is_not_in_a_handle;
1761 FixedArray* match_info_array = last_match_info_handle->elements();
1762
1763 ASSERT_EQ(capture_count * 2 + 2,
1764 RegExpImpl::GetLastCaptureCount(match_info_array));
1765 start = RegExpImpl::GetCapture(match_info_array, 0);
1766 end = RegExpImpl::GetCapture(match_info_array, 1);
1767 }
1768
1769 if (prev < start) {
1770 builder.AddSubjectSlice(prev, start);
1771 }
1772 compiled_replacement.Apply(&builder,
1773 start,
1774 end,
1775 last_match_info_handle);
1776 prev = end;
1777
1778 // Only continue checking for global regexps.
1779 if (!is_global) break;
1780
1781 // Continue from where the match ended, unless it was an empty match.
1782 int next = end;
1783 if (start == end) {
1784 next = end + 1;
1785 if (next > length) break;
1786 }
1787
1788 match = RegExpImpl::Exec(regexp_handle,
1789 subject_handle,
1790 next,
1791 last_match_info_handle);
1792 if (match.is_null()) {
1793 return Failure::Exception();
1794 }
1795 matched = !match->IsNull();
1796 } while (matched);
1797
1798 if (prev < length) {
1799 builder.AddSubjectSlice(prev, length);
1800 }
1801
1802 return *(builder.ToString());
1803}
1804
1805
1806static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1807 ASSERT(args.length() == 4);
1808
1809 CONVERT_CHECKED(String, subject, args[0]);
1810 if (!subject->IsFlat()) {
1811 Object* flat_subject = subject->TryFlatten();
1812 if (flat_subject->IsFailure()) {
1813 return flat_subject;
1814 }
1815 subject = String::cast(flat_subject);
1816 }
1817
1818 CONVERT_CHECKED(String, replacement, args[2]);
1819 if (!replacement->IsFlat()) {
1820 Object* flat_replacement = replacement->TryFlatten();
1821 if (flat_replacement->IsFailure()) {
1822 return flat_replacement;
1823 }
1824 replacement = String::cast(flat_replacement);
1825 }
1826
1827 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1828 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1829
1830 ASSERT(last_match_info->HasFastElements());
1831
1832 return StringReplaceRegExpWithString(subject,
1833 regexp,
1834 replacement,
1835 last_match_info);
1836}
1837
1838
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001839
ager@chromium.org7c537e22008-10-16 08:43:32 +00001840// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1841// limit, we can fix the size of tables.
1842static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001843// Reduce alphabet to this size.
1844static const int kBMAlphabetSize = 0x100;
1845// For patterns below this length, the skip length of Boyer-Moore is too short
1846// to compensate for the algorithmic overhead compared to simple brute force.
1847static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001848
ager@chromium.org7c537e22008-10-16 08:43:32 +00001849// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1850// shift. Only allows the last kBMMaxShift characters of the needle
1851// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001852class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001853 public:
1854 BMGoodSuffixBuffers() {}
1855 inline void init(int needle_length) {
1856 ASSERT(needle_length > 1);
1857 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1858 int len = needle_length - start;
1859 biased_suffixes_ = suffixes_ - start;
1860 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1861 for (int i = 0; i <= len; i++) {
1862 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001863 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001864 }
1865 inline int& suffix(int index) {
1866 ASSERT(biased_suffixes_ + index >= suffixes_);
1867 return biased_suffixes_[index];
1868 }
1869 inline int& shift(int index) {
1870 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1871 return biased_good_suffix_shift_[index];
1872 }
1873 private:
1874 int suffixes_[kBMMaxShift + 1];
1875 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001876 int* biased_suffixes_;
1877 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001878 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1879};
1880
1881// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001882static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001883static BMGoodSuffixBuffers bmgs_buffers;
1884
1885// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001886template <typename pchar>
1887static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1888 int start) {
1889 // Run forwards to populate bad_char_table, so that *last* instance
1890 // of character equivalence class is the one registered.
1891 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001892 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1893 : kBMAlphabetSize;
1894 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001895 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001896 } else {
1897 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001898 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001899 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001900 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001901 for (int i = start; i < pattern.length() - 1; i++) {
1902 pchar c = pattern[i];
1903 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001904 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001905 }
1906}
1907
1908template <typename pchar>
1909static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001910 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001911 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001912 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001913 // Compute Good Suffix tables.
1914 bmgs_buffers.init(m);
1915
1916 bmgs_buffers.shift(m-1) = 1;
1917 bmgs_buffers.suffix(m) = m + 1;
1918 pchar last_char = pattern[m - 1];
1919 int suffix = m + 1;
1920 for (int i = m; i > start;) {
1921 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1922 if (bmgs_buffers.shift(suffix) == len) {
1923 bmgs_buffers.shift(suffix) = suffix - i;
1924 }
1925 suffix = bmgs_buffers.suffix(suffix);
1926 }
1927 i--;
1928 suffix--;
1929 bmgs_buffers.suffix(i) = suffix;
1930 if (suffix == m) {
1931 // No suffix to extend, so we check against last_char only.
1932 while (i > start && pattern[i - 1] != last_char) {
1933 if (bmgs_buffers.shift(m) == len) {
1934 bmgs_buffers.shift(m) = m - i;
1935 }
1936 i--;
1937 bmgs_buffers.suffix(i) = m;
1938 }
1939 if (i > start) {
1940 i--;
1941 suffix--;
1942 bmgs_buffers.suffix(i) = suffix;
1943 }
1944 }
1945 }
1946 if (suffix < m) {
1947 for (int i = start; i <= m; i++) {
1948 if (bmgs_buffers.shift(i) == len) {
1949 bmgs_buffers.shift(i) = suffix - start;
1950 }
1951 if (i == suffix) {
1952 suffix = bmgs_buffers.suffix(suffix);
1953 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001954 }
1955 }
1956}
1957
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001958template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001959static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001960 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001961 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001962 }
1963 if (sizeof(pchar) == 1) {
1964 if (char_code > String::kMaxAsciiCharCode) {
1965 return -1;
1966 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001968 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001969 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001970}
1971
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001972// Restricted simplified Boyer-Moore string matching.
1973// Uses only the bad-shift table of Boyer-Moore and only uses it
1974// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001975template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001976static int BoyerMooreHorspool(Vector<const schar> subject,
1977 Vector<const pchar> pattern,
1978 int start_index,
1979 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001980 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001981 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001982 // Only preprocess at most kBMMaxShift last characters of pattern.
1983 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001984
ager@chromium.org7c537e22008-10-16 08:43:32 +00001985 BoyerMoorePopulateBadCharTable(pattern, start);
1986
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001987 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001988 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001989 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001990 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001991 // Perform search
1992 for (idx = start_index; idx <= n - m;) {
1993 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001994 int c;
1995 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001996 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001997 int shift = j - bc_occ;
1998 idx += shift;
1999 badness += 1 - shift; // at most zero, so badness cannot increase.
2000 if (idx > n - m) {
2001 *complete = true;
2002 return -1;
2003 }
2004 }
2005 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002006 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002007 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002008 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002009 return idx;
2010 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002011 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002012 // Badness increases by the number of characters we have
2013 // checked, and decreases by the number of characters we
2014 // can skip by shifting. It's a measure of how we are doing
2015 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002016 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002017 if (badness > 0) {
2018 *complete = false;
2019 return idx;
2020 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002021 }
2022 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002023 *complete = true;
2024 return -1;
2025}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002026
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002027
2028template <typename schar, typename pchar>
2029static int BoyerMooreIndexOf(Vector<const schar> subject,
2030 Vector<const pchar> pattern,
2031 int idx) {
2032 int n = subject.length();
2033 int m = pattern.length();
2034 // Only preprocess at most kBMMaxShift last characters of pattern.
2035 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2036
2037 // Build the Good Suffix table and continue searching.
2038 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2039 pchar last_char = pattern[m - 1];
2040 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002041 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002042 int j = m - 1;
2043 schar c;
2044 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002045 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002046 idx += shift;
2047 if (idx > n - m) {
2048 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002049 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002050 }
2051 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2052 if (j < 0) {
2053 return idx;
2054 } else if (j < start) {
2055 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002056 // Fall back on BMH shift.
2057 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002058 } else {
2059 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002060 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002061 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002062 if (gs_shift > shift) {
2063 shift = gs_shift;
2064 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002065 idx += shift;
2066 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002067 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002068
2069 return -1;
2070}
2071
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002072
2073template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002074static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002075 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002076 int start_index) {
2077 for (int i = start_index, n = string.length(); i < n; i++) {
2078 if (pattern_char == string[i]) {
2079 return i;
2080 }
2081 }
2082 return -1;
2083}
2084
2085// Trivial string search for shorter strings.
2086// On return, if "complete" is set to true, the return value is the
2087// final result of searching for the patter in the subject.
2088// If "complete" is set to false, the return value is the index where
2089// further checking should start, i.e., it's guaranteed that the pattern
2090// does not occur at a position prior to the returned index.
2091template <typename pchar, typename schar>
2092static int SimpleIndexOf(Vector<const schar> subject,
2093 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002094 int idx,
2095 bool* complete) {
2096 // Badness is a count of how much work we have done. When we have
2097 // done enough work we decide it's probably worth switching to a better
2098 // algorithm.
2099 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002100 // We know our pattern is at least 2 characters, we cache the first so
2101 // the common case of the first character not matching is faster.
2102 pchar pattern_first_char = pattern[0];
2103
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002104 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2105 badness++;
2106 if (badness > 0) {
2107 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002108 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002109 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002110 if (subject[i] != pattern_first_char) continue;
2111 int j = 1;
2112 do {
2113 if (pattern[j] != subject[i+j]) {
2114 break;
2115 }
2116 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002117 } while (j < pattern.length());
2118 if (j == pattern.length()) {
2119 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002120 return i;
2121 }
2122 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002123 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002124 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002125 return -1;
2126}
2127
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002128// Simple indexOf that never bails out. For short patterns only.
2129template <typename pchar, typename schar>
2130static int SimpleIndexOf(Vector<const schar> subject,
2131 Vector<const pchar> pattern,
2132 int idx) {
2133 pchar pattern_first_char = pattern[0];
2134 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2135 if (subject[i] != pattern_first_char) continue;
2136 int j = 1;
2137 do {
2138 if (pattern[j] != subject[i+j]) {
2139 break;
2140 }
2141 j++;
2142 } while (j < pattern.length());
2143 if (j == pattern.length()) {
2144 return i;
2145 }
2146 }
2147 return -1;
2148}
2149
2150
2151// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002152template <typename schar, typename pchar>
2153static int StringMatchStrategy(Vector<const schar> sub,
2154 Vector<const pchar> pat,
2155 int start_index) {
2156 ASSERT(pat.length() > 1);
2157
2158 // We have an ASCII haystack and a non-ASCII needle. Check if there
2159 // really is a non-ASCII character in the needle and bail out if there
2160 // is.
2161 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2162 for (int i = 0; i < pat.length(); i++) {
2163 uc16 c = pat[i];
2164 if (c > String::kMaxAsciiCharCode) {
2165 return -1;
2166 }
2167 }
2168 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002169 if (pat.length() < kBMMinPatternLength) {
2170 // We don't believe fancy searching can ever be more efficient.
2171 // The max shift of Boyer-Moore on a pattern of this length does
2172 // not compensate for the overhead.
2173 return SimpleIndexOf(sub, pat, start_index);
2174 }
2175 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002176 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002177 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2178 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002179 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002180 if (complete) return idx;
2181 return BoyerMooreIndexOf(sub, pat, idx);
2182}
2183
2184// Perform string match of pattern on subject, starting at start index.
2185// Caller must ensure that 0 <= start_index <= sub->length(),
2186// and should check that pat->length() + start_index <= sub->length()
2187int Runtime::StringMatch(Handle<String> sub,
2188 Handle<String> pat,
2189 int start_index) {
2190 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002191 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002192
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002193 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002194 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002195
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002196 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002197 if (start_index + pattern_length > subject_length) return -1;
2198
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002199 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002200 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002201 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002202 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002203 // character patterns linear search is necessary, so any smart
2204 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002205 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002206 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002207 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002208 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002209 if (pchar > String::kMaxAsciiCharCode) {
2210 return -1;
2211 }
2212 Vector<const char> ascii_vector =
2213 sub->ToAsciiVector().SubVector(start_index, subject_length);
2214 const void* pos = memchr(ascii_vector.start(),
2215 static_cast<const char>(pchar),
2216 static_cast<size_t>(ascii_vector.length()));
2217 if (pos == NULL) {
2218 return -1;
2219 }
2220 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2221 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002222 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002223 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002224 }
2225
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002226 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002227 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002228 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002229
ager@chromium.org7c537e22008-10-16 08:43:32 +00002230 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2231 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002232 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002233 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002234 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002235 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002236 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002237 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002238 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002239 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002240 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002241 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002242 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002243 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002244}
2245
2246
2247static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002248 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002249 ASSERT(args.length() == 3);
2250
ager@chromium.org7c537e22008-10-16 08:43:32 +00002251 CONVERT_ARG_CHECKED(String, sub, 0);
2252 CONVERT_ARG_CHECKED(String, pat, 1);
2253
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002254 Object* index = args[2];
2255 uint32_t start_index;
2256 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2257
ager@chromium.org870a0b62008-11-04 11:43:05 +00002258 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002259 int position = Runtime::StringMatch(sub, pat, start_index);
2260 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002261}
2262
2263
2264static Object* Runtime_StringLastIndexOf(Arguments args) {
2265 NoHandleAllocation ha;
2266 ASSERT(args.length() == 3);
2267
2268 CONVERT_CHECKED(String, sub, args[0]);
2269 CONVERT_CHECKED(String, pat, args[1]);
2270 Object* index = args[2];
2271
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002272 sub->TryFlattenIfNotFlat();
2273 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002274
2275 uint32_t start_index;
2276 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2277
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002278 uint32_t pattern_length = pat->length();
2279 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002280
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002281 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002282 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002283 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002284
2285 for (int i = start_index; i >= 0; i--) {
2286 bool found = true;
2287 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002288 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002289 found = false;
2290 break;
2291 }
2292 }
2293 if (found) return Smi::FromInt(i);
2294 }
2295
2296 return Smi::FromInt(-1);
2297}
2298
2299
2300static Object* Runtime_StringLocaleCompare(Arguments args) {
2301 NoHandleAllocation ha;
2302 ASSERT(args.length() == 2);
2303
2304 CONVERT_CHECKED(String, str1, args[0]);
2305 CONVERT_CHECKED(String, str2, args[1]);
2306
2307 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002308 int str1_length = str1->length();
2309 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002310
2311 // Decide trivial cases without flattening.
2312 if (str1_length == 0) {
2313 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2314 return Smi::FromInt(-str2_length);
2315 } else {
2316 if (str2_length == 0) return Smi::FromInt(str1_length);
2317 }
2318
2319 int end = str1_length < str2_length ? str1_length : str2_length;
2320
2321 // No need to flatten if we are going to find the answer on the first
2322 // character. At this point we know there is at least one character
2323 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002324 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002325 if (d != 0) return Smi::FromInt(d);
2326
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002327 str1->TryFlattenIfNotFlat();
2328 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002329
2330 static StringInputBuffer buf1;
2331 static StringInputBuffer buf2;
2332
2333 buf1.Reset(str1);
2334 buf2.Reset(str2);
2335
2336 for (int i = 0; i < end; i++) {
2337 uint16_t char1 = buf1.GetNext();
2338 uint16_t char2 = buf2.GetNext();
2339 if (char1 != char2) return Smi::FromInt(char1 - char2);
2340 }
2341
2342 return Smi::FromInt(str1_length - str2_length);
2343}
2344
2345
2346static Object* Runtime_StringSlice(Arguments args) {
2347 NoHandleAllocation ha;
2348 ASSERT(args.length() == 3);
2349
2350 CONVERT_CHECKED(String, value, args[0]);
2351 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2352 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2353
2354 int start = FastD2I(from_number);
2355 int end = FastD2I(to_number);
2356
2357 RUNTIME_ASSERT(end >= start);
2358 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002359 RUNTIME_ASSERT(end <= value->length());
2360 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002361}
2362
2363
ager@chromium.org41826e72009-03-30 13:30:57 +00002364static Object* Runtime_StringMatch(Arguments args) {
2365 ASSERT_EQ(3, args.length());
2366
2367 CONVERT_ARG_CHECKED(String, subject, 0);
2368 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2369 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2370 HandleScope handles;
2371
2372 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2373
2374 if (match.is_null()) {
2375 return Failure::Exception();
2376 }
2377 if (match->IsNull()) {
2378 return Heap::null_value();
2379 }
2380 int length = subject->length();
2381
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002382 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002383 ZoneList<int> offsets(8);
2384 do {
2385 int start;
2386 int end;
2387 {
2388 AssertNoAllocation no_alloc;
2389 FixedArray* elements = regexp_info->elements();
2390 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2391 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2392 }
2393 offsets.Add(start);
2394 offsets.Add(end);
2395 int index = start < end ? end : end + 1;
2396 if (index > length) break;
2397 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2398 if (match.is_null()) {
2399 return Failure::Exception();
2400 }
2401 } while (!match->IsNull());
2402 int matches = offsets.length() / 2;
2403 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2404 for (int i = 0; i < matches ; i++) {
2405 int from = offsets.at(i * 2);
2406 int to = offsets.at(i * 2 + 1);
2407 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2408 }
2409 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2410 result->set_length(Smi::FromInt(matches));
2411 return *result;
2412}
2413
2414
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002415static Object* Runtime_NumberToRadixString(Arguments args) {
2416 NoHandleAllocation ha;
2417 ASSERT(args.length() == 2);
2418
2419 CONVERT_DOUBLE_CHECKED(value, args[0]);
2420 if (isnan(value)) {
2421 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2422 }
2423 if (isinf(value)) {
2424 if (value < 0) {
2425 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2426 }
2427 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2428 }
2429 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2430 int radix = FastD2I(radix_number);
2431 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2432 char* str = DoubleToRadixCString(value, radix);
2433 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2434 DeleteArray(str);
2435 return result;
2436}
2437
2438
2439static Object* Runtime_NumberToFixed(Arguments args) {
2440 NoHandleAllocation ha;
2441 ASSERT(args.length() == 2);
2442
2443 CONVERT_DOUBLE_CHECKED(value, args[0]);
2444 if (isnan(value)) {
2445 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2446 }
2447 if (isinf(value)) {
2448 if (value < 0) {
2449 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2450 }
2451 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2452 }
2453 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2454 int f = FastD2I(f_number);
2455 RUNTIME_ASSERT(f >= 0);
2456 char* str = DoubleToFixedCString(value, f);
2457 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2458 DeleteArray(str);
2459 return res;
2460}
2461
2462
2463static Object* Runtime_NumberToExponential(Arguments args) {
2464 NoHandleAllocation ha;
2465 ASSERT(args.length() == 2);
2466
2467 CONVERT_DOUBLE_CHECKED(value, args[0]);
2468 if (isnan(value)) {
2469 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2470 }
2471 if (isinf(value)) {
2472 if (value < 0) {
2473 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2474 }
2475 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2476 }
2477 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2478 int f = FastD2I(f_number);
2479 RUNTIME_ASSERT(f >= -1 && f <= 20);
2480 char* str = DoubleToExponentialCString(value, f);
2481 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2482 DeleteArray(str);
2483 return res;
2484}
2485
2486
2487static Object* Runtime_NumberToPrecision(Arguments args) {
2488 NoHandleAllocation ha;
2489 ASSERT(args.length() == 2);
2490
2491 CONVERT_DOUBLE_CHECKED(value, args[0]);
2492 if (isnan(value)) {
2493 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2494 }
2495 if (isinf(value)) {
2496 if (value < 0) {
2497 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2498 }
2499 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2500 }
2501 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2502 int f = FastD2I(f_number);
2503 RUNTIME_ASSERT(f >= 1 && f <= 21);
2504 char* str = DoubleToPrecisionCString(value, f);
2505 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2506 DeleteArray(str);
2507 return res;
2508}
2509
2510
2511// Returns a single character string where first character equals
2512// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002513static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002514 if (index < static_cast<uint32_t>(string->length())) {
2515 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002516 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002517 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002518 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002519 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002520}
2521
2522
2523Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2524 // Handle [] indexing on Strings
2525 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002526 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2527 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002528 }
2529
2530 // Handle [] indexing on String objects
2531 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002532 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2533 Handle<Object> result =
2534 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2535 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002536 }
2537
2538 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002539 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002540 return prototype->GetElement(index);
2541 }
2542
2543 return object->GetElement(index);
2544}
2545
2546
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002547Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2548 HandleScope scope;
2549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002550 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002551 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002552 Handle<Object> error =
2553 Factory::NewTypeError("non_object_property_load",
2554 HandleVector(args, 2));
2555 return Top::Throw(*error);
2556 }
2557
2558 // Check if the given key is an array index.
2559 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002560 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561 return GetElementOrCharAt(object, index);
2562 }
2563
2564 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002565 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002566 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002567 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002568 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569 bool has_pending_exception = false;
2570 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002571 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002572 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002573 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002574 }
2575
ager@chromium.org32912102009-01-16 10:38:43 +00002576 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002577 // the element if so.
2578 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002579 return GetElementOrCharAt(object, index);
2580 } else {
2581 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002582 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002583 }
2584}
2585
2586
2587static Object* Runtime_GetProperty(Arguments args) {
2588 NoHandleAllocation ha;
2589 ASSERT(args.length() == 2);
2590
2591 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002592 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002593
2594 return Runtime::GetObjectProperty(object, key);
2595}
2596
2597
ager@chromium.org7c537e22008-10-16 08:43:32 +00002598
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002599// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002600static Object* Runtime_KeyedGetProperty(Arguments args) {
2601 NoHandleAllocation ha;
2602 ASSERT(args.length() == 2);
2603
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002604 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002605 // itself.
2606 //
2607 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002608 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002609 // global proxy object never has properties. This is the case
2610 // because the global proxy object forwards everything to its hidden
2611 // prototype including local lookups.
2612 //
2613 // Additionally, we need to make sure that we do not cache results
2614 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 if (args[0]->IsJSObject() &&
2616 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002617 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002618 args[1]->IsString()) {
2619 JSObject* receiver = JSObject::cast(args[0]);
2620 String* key = String::cast(args[1]);
2621 if (receiver->HasFastProperties()) {
2622 // Attempt to use lookup cache.
2623 Object* obj = Heap::GetKeyedLookupCache();
2624 if (obj->IsFailure()) return obj;
2625 LookupCache* cache = LookupCache::cast(obj);
2626 Map* receiver_map = receiver->map();
2627 int offset = cache->Lookup(receiver_map, key);
2628 if (offset != LookupCache::kNotFound) {
2629 Object* value = receiver->FastPropertyAt(offset);
2630 return value->IsTheHole() ? Heap::undefined_value() : value;
2631 }
2632 // Lookup cache miss. Perform lookup and update the cache if
2633 // appropriate.
2634 LookupResult result;
2635 receiver->LocalLookup(key, &result);
2636 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2637 int offset = result.GetFieldIndex();
2638 Object* obj = cache->Put(receiver_map, key, offset);
2639 if (obj->IsFailure()) return obj;
2640 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
2641 Object* value = receiver->FastPropertyAt(offset);
2642 return value->IsTheHole() ? Heap::undefined_value() : value;
2643 }
2644 } else {
2645 // Attempt dictionary lookup.
2646 Dictionary* dictionary = receiver->property_dictionary();
2647 int entry = dictionary->FindStringEntry(key);
2648 if ((entry != DescriptorArray::kNotFound) &&
2649 (dictionary->DetailsAt(entry).type() == NORMAL)) {
2650 return dictionary->ValueAt(entry);
2651 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002652 }
2653 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002654
2655 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002656 return Runtime::GetObjectProperty(args.at<Object>(0),
2657 args.at<Object>(1));
2658}
2659
2660
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661Object* Runtime::SetObjectProperty(Handle<Object> object,
2662 Handle<Object> key,
2663 Handle<Object> value,
2664 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002665 HandleScope scope;
2666
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002667 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002668 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002669 Handle<Object> error =
2670 Factory::NewTypeError("non_object_property_store",
2671 HandleVector(args, 2));
2672 return Top::Throw(*error);
2673 }
2674
2675 // If the object isn't a JavaScript object, we ignore the store.
2676 if (!object->IsJSObject()) return *value;
2677
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002678 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2679
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002680 // Check if the given key is an array index.
2681 uint32_t index;
2682 if (Array::IndexFromObject(*key, &index)) {
2683 ASSERT(attr == NONE);
2684
2685 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2686 // of a string using [] notation. We need to support this too in
2687 // JavaScript.
2688 // In the case of a String object we just need to redirect the assignment to
2689 // the underlying string if the index is in range. Since the underlying
2690 // string does nothing with the assignment then we can ignore such
2691 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002692 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002693 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002694 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002695
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002696 Handle<Object> result = SetElement(js_object, index, value);
2697 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698 return *value;
2699 }
2700
2701 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002702 Handle<Object> result;
2703 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002704 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002705 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002706 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002707 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002708 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002709 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002710 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002711 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002712 return *value;
2713 }
2714
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002715 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002716 bool has_pending_exception = false;
2717 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2718 if (has_pending_exception) return Failure::Exception();
2719 Handle<String> name = Handle<String>::cast(converted);
2720
2721 if (name->AsArrayIndex(&index)) {
2722 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002723 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002724 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002725 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002726 }
2727}
2728
2729
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002730Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2731 Handle<Object> key,
2732 Handle<Object> value,
2733 PropertyAttributes attr) {
2734 HandleScope scope;
2735
2736 // Check if the given key is an array index.
2737 uint32_t index;
2738 if (Array::IndexFromObject(*key, &index)) {
2739 ASSERT(attr == NONE);
2740
2741 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2742 // of a string using [] notation. We need to support this too in
2743 // JavaScript.
2744 // In the case of a String object we just need to redirect the assignment to
2745 // the underlying string if the index is in range. Since the underlying
2746 // string does nothing with the assignment then we can ignore such
2747 // assignments.
2748 if (js_object->IsStringObjectWithCharacterAt(index)) {
2749 return *value;
2750 }
2751
2752 return js_object->SetElement(index, *value);
2753 }
2754
2755 if (key->IsString()) {
2756 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2757 ASSERT(attr == NONE);
2758 return js_object->SetElement(index, *value);
2759 } else {
2760 Handle<String> key_string = Handle<String>::cast(key);
2761 key_string->TryFlattenIfNotFlat();
2762 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2763 *value,
2764 attr);
2765 }
2766 }
2767
2768 // Call-back into JavaScript to convert the key to a string.
2769 bool has_pending_exception = false;
2770 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2771 if (has_pending_exception) return Failure::Exception();
2772 Handle<String> name = Handle<String>::cast(converted);
2773
2774 if (name->AsArrayIndex(&index)) {
2775 ASSERT(attr == NONE);
2776 return js_object->SetElement(index, *value);
2777 } else {
2778 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2779 }
2780}
2781
2782
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002783static Object* Runtime_SetProperty(Arguments args) {
2784 NoHandleAllocation ha;
2785 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2786
2787 Handle<Object> object = args.at<Object>(0);
2788 Handle<Object> key = args.at<Object>(1);
2789 Handle<Object> value = args.at<Object>(2);
2790
2791 // Compute attributes.
2792 PropertyAttributes attributes = NONE;
2793 if (args.length() == 4) {
2794 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002795 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002796 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002797 RUNTIME_ASSERT(
2798 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2799 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002800 }
2801 return Runtime::SetObjectProperty(object, key, value, attributes);
2802}
2803
2804
2805// Set a local property, even if it is READ_ONLY. If the property does not
2806// exist, it will be added with attributes NONE.
2807static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2808 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002809 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002810 CONVERT_CHECKED(JSObject, object, args[0]);
2811 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002812 // Compute attributes.
2813 PropertyAttributes attributes = NONE;
2814 if (args.length() == 4) {
2815 CONVERT_CHECKED(Smi, value_obj, args[3]);
2816 int unchecked_value = value_obj->value();
2817 // Only attribute bits should be set.
2818 RUNTIME_ASSERT(
2819 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2820 attributes = static_cast<PropertyAttributes>(unchecked_value);
2821 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002822
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002823 return object->
2824 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002825}
2826
2827
2828static Object* Runtime_DeleteProperty(Arguments args) {
2829 NoHandleAllocation ha;
2830 ASSERT(args.length() == 2);
2831
2832 CONVERT_CHECKED(JSObject, object, args[0]);
2833 CONVERT_CHECKED(String, key, args[1]);
2834 return object->DeleteProperty(key);
2835}
2836
2837
ager@chromium.org9085a012009-05-11 19:22:57 +00002838static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2839 Handle<String> key) {
2840 if (object->HasLocalProperty(*key)) return Heap::true_value();
2841 // Handle hidden prototypes. If there's a hidden prototype above this thing
2842 // then we have to check it for properties, because they are supposed to
2843 // look like they are on this object.
2844 Handle<Object> proto(object->GetPrototype());
2845 if (proto->IsJSObject() &&
2846 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2847 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2848 }
2849 return Heap::false_value();
2850}
2851
2852
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002853static Object* Runtime_HasLocalProperty(Arguments args) {
2854 NoHandleAllocation ha;
2855 ASSERT(args.length() == 2);
2856 CONVERT_CHECKED(String, key, args[1]);
2857
ager@chromium.org9085a012009-05-11 19:22:57 +00002858 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002859 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002860 if (obj->IsJSObject()) {
2861 JSObject* object = JSObject::cast(obj);
2862 // Fast case - no interceptors.
2863 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2864 // Slow case. Either it's not there or we have an interceptor. We should
2865 // have handles for this kind of deal.
2866 HandleScope scope;
2867 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2868 Handle<String>(key));
2869 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002870 // Well, there is one exception: Handle [] on strings.
2871 uint32_t index;
2872 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002873 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002874 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875 return Heap::true_value();
2876 }
2877 }
2878 return Heap::false_value();
2879}
2880
2881
2882static Object* Runtime_HasProperty(Arguments args) {
2883 NoHandleAllocation na;
2884 ASSERT(args.length() == 2);
2885
2886 // Only JS objects can have properties.
2887 if (args[0]->IsJSObject()) {
2888 JSObject* object = JSObject::cast(args[0]);
2889 CONVERT_CHECKED(String, key, args[1]);
2890 if (object->HasProperty(key)) return Heap::true_value();
2891 }
2892 return Heap::false_value();
2893}
2894
2895
2896static Object* Runtime_HasElement(Arguments args) {
2897 NoHandleAllocation na;
2898 ASSERT(args.length() == 2);
2899
2900 // Only JS objects can have elements.
2901 if (args[0]->IsJSObject()) {
2902 JSObject* object = JSObject::cast(args[0]);
2903 CONVERT_CHECKED(Smi, index_obj, args[1]);
2904 uint32_t index = index_obj->value();
2905 if (object->HasElement(index)) return Heap::true_value();
2906 }
2907 return Heap::false_value();
2908}
2909
2910
2911static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2912 NoHandleAllocation ha;
2913 ASSERT(args.length() == 2);
2914
2915 CONVERT_CHECKED(JSObject, object, args[0]);
2916 CONVERT_CHECKED(String, key, args[1]);
2917
2918 uint32_t index;
2919 if (key->AsArrayIndex(&index)) {
2920 return Heap::ToBoolean(object->HasElement(index));
2921 }
2922
ager@chromium.org870a0b62008-11-04 11:43:05 +00002923 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2924 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002925}
2926
2927
2928static Object* Runtime_GetPropertyNames(Arguments args) {
2929 HandleScope scope;
2930 ASSERT(args.length() == 1);
2931
2932 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2933 Handle<JSObject> object(raw_object);
2934 return *GetKeysFor(object);
2935}
2936
2937
2938// Returns either a FixedArray as Runtime_GetPropertyNames,
2939// or, if the given object has an enum cache that contains
2940// all enumerable properties of the object and its prototypes
2941// have none, the map of the object. This is used to speed up
2942// the check for deletions during a for-in.
2943static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2944 ASSERT(args.length() == 1);
2945
2946 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2947
2948 if (raw_object->IsSimpleEnum()) return raw_object->map();
2949
2950 HandleScope scope;
2951 Handle<JSObject> object(raw_object);
2952 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2953
2954 // Test again, since cache may have been built by preceding call.
2955 if (object->IsSimpleEnum()) return object->map();
2956
2957 return *content;
2958}
2959
2960
2961static Object* Runtime_GetArgumentsProperty(Arguments args) {
2962 NoHandleAllocation ha;
2963 ASSERT(args.length() == 1);
2964
2965 // Compute the frame holding the arguments.
2966 JavaScriptFrameIterator it;
2967 it.AdvanceToArgumentsFrame();
2968 JavaScriptFrame* frame = it.frame();
2969
2970 // Get the actual number of provided arguments.
2971 const uint32_t n = frame->GetProvidedParametersCount();
2972
2973 // Try to convert the key to an index. If successful and within
2974 // index return the the argument from the frame.
2975 uint32_t index;
2976 if (Array::IndexFromObject(args[0], &index) && index < n) {
2977 return frame->GetParameter(index);
2978 }
2979
2980 // Convert the key to a string.
2981 HandleScope scope;
2982 bool exception = false;
2983 Handle<Object> converted =
2984 Execution::ToString(args.at<Object>(0), &exception);
2985 if (exception) return Failure::Exception();
2986 Handle<String> key = Handle<String>::cast(converted);
2987
2988 // Try to convert the string key into an array index.
2989 if (key->AsArrayIndex(&index)) {
2990 if (index < n) {
2991 return frame->GetParameter(index);
2992 } else {
2993 return Top::initial_object_prototype()->GetElement(index);
2994 }
2995 }
2996
2997 // Handle special arguments properties.
2998 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2999 if (key->Equals(Heap::callee_symbol())) return frame->function();
3000
3001 // Lookup in the initial Object.prototype object.
3002 return Top::initial_object_prototype()->GetProperty(*key);
3003}
3004
3005
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003006static Object* Runtime_ToFastProperties(Arguments args) {
3007 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003008 Handle<Object> object = args.at<Object>(0);
3009 if (object->IsJSObject()) {
3010 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3011 js_object->TransformToFastProperties(0);
3012 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003013 return *object;
3014}
3015
3016
3017static Object* Runtime_ToSlowProperties(Arguments args) {
3018 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003019 Handle<Object> object = args.at<Object>(0);
3020 if (object->IsJSObject()) {
3021 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3022 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
3023 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003024 return *object;
3025}
3026
3027
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003028static Object* Runtime_ToBool(Arguments args) {
3029 NoHandleAllocation ha;
3030 ASSERT(args.length() == 1);
3031
3032 return args[0]->ToBoolean();
3033}
3034
3035
3036// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3037// Possible optimizations: put the type string into the oddballs.
3038static Object* Runtime_Typeof(Arguments args) {
3039 NoHandleAllocation ha;
3040
3041 Object* obj = args[0];
3042 if (obj->IsNumber()) return Heap::number_symbol();
3043 HeapObject* heap_obj = HeapObject::cast(obj);
3044
3045 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003046 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003047
3048 InstanceType instance_type = heap_obj->map()->instance_type();
3049 if (instance_type < FIRST_NONSTRING_TYPE) {
3050 return Heap::string_symbol();
3051 }
3052
3053 switch (instance_type) {
3054 case ODDBALL_TYPE:
3055 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3056 return Heap::boolean_symbol();
3057 }
3058 if (heap_obj->IsNull()) {
3059 return Heap::object_symbol();
3060 }
3061 ASSERT(heap_obj->IsUndefined());
3062 return Heap::undefined_symbol();
3063 case JS_FUNCTION_TYPE:
3064 return Heap::function_symbol();
3065 default:
3066 // For any kind of object not handled above, the spec rule for
3067 // host objects gives that it is okay to return "object"
3068 return Heap::object_symbol();
3069 }
3070}
3071
3072
3073static Object* Runtime_StringToNumber(Arguments args) {
3074 NoHandleAllocation ha;
3075 ASSERT(args.length() == 1);
3076 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003077 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003078 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3079}
3080
3081
3082static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3083 NoHandleAllocation ha;
3084 ASSERT(args.length() == 1);
3085
3086 CONVERT_CHECKED(JSArray, codes, args[0]);
3087 int length = Smi::cast(codes->length())->value();
3088
3089 // Check if the string can be ASCII.
3090 int i;
3091 for (i = 0; i < length; i++) {
3092 Object* element = codes->GetElement(i);
3093 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3094 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3095 break;
3096 }
3097
3098 Object* object = NULL;
3099 if (i == length) { // The string is ASCII.
3100 object = Heap::AllocateRawAsciiString(length);
3101 } else { // The string is not ASCII.
3102 object = Heap::AllocateRawTwoByteString(length);
3103 }
3104
3105 if (object->IsFailure()) return object;
3106 String* result = String::cast(object);
3107 for (int i = 0; i < length; i++) {
3108 Object* element = codes->GetElement(i);
3109 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003110 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003111 }
3112 return result;
3113}
3114
3115
3116// kNotEscaped is generated by the following:
3117//
3118// #!/bin/perl
3119// for (my $i = 0; $i < 256; $i++) {
3120// print "\n" if $i % 16 == 0;
3121// my $c = chr($i);
3122// my $escaped = 1;
3123// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3124// print $escaped ? "0, " : "1, ";
3125// }
3126
3127
3128static bool IsNotEscaped(uint16_t character) {
3129 // Only for 8 bit characters, the rest are always escaped (in a different way)
3130 ASSERT(character < 256);
3131 static const char kNotEscaped[256] = {
3132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3135 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3136 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3137 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3138 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3139 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3148 };
3149 return kNotEscaped[character] != 0;
3150}
3151
3152
3153static Object* Runtime_URIEscape(Arguments args) {
3154 const char hex_chars[] = "0123456789ABCDEF";
3155 NoHandleAllocation ha;
3156 ASSERT(args.length() == 1);
3157 CONVERT_CHECKED(String, source, args[0]);
3158
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003159 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003160
3161 int escaped_length = 0;
3162 int length = source->length();
3163 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003164 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003165 buffer->Reset(source);
3166 while (buffer->has_more()) {
3167 uint16_t character = buffer->GetNext();
3168 if (character >= 256) {
3169 escaped_length += 6;
3170 } else if (IsNotEscaped(character)) {
3171 escaped_length++;
3172 } else {
3173 escaped_length += 3;
3174 }
3175 // We don't allow strings that are longer than Smi range.
3176 if (!Smi::IsValid(escaped_length)) {
3177 Top::context()->mark_out_of_memory();
3178 return Failure::OutOfMemoryException();
3179 }
3180 }
3181 }
3182 // No length change implies no change. Return original string if no change.
3183 if (escaped_length == length) {
3184 return source;
3185 }
3186 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3187 if (o->IsFailure()) return o;
3188 String* destination = String::cast(o);
3189 int dest_position = 0;
3190
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003191 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003192 buffer->Rewind();
3193 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003194 uint16_t chr = buffer->GetNext();
3195 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003196 destination->Set(dest_position, '%');
3197 destination->Set(dest_position+1, 'u');
3198 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3199 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3200 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3201 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003202 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003203 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003204 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003205 dest_position++;
3206 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003207 destination->Set(dest_position, '%');
3208 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3209 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003210 dest_position += 3;
3211 }
3212 }
3213 return destination;
3214}
3215
3216
3217static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3218 static const signed char kHexValue['g'] = {
3219 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3220 -1, -1, -1, -1, -1, -1, -1, -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 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3223 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3224 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3225 -1, 10, 11, 12, 13, 14, 15 };
3226
3227 if (character1 > 'f') return -1;
3228 int hi = kHexValue[character1];
3229 if (hi == -1) return -1;
3230 if (character2 > 'f') return -1;
3231 int lo = kHexValue[character2];
3232 if (lo == -1) return -1;
3233 return (hi << 4) + lo;
3234}
3235
3236
ager@chromium.org870a0b62008-11-04 11:43:05 +00003237static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003238 int i,
3239 int length,
3240 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003241 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003242 int32_t hi = 0;
3243 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003244 if (character == '%' &&
3245 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003246 source->Get(i + 1) == 'u' &&
3247 (hi = TwoDigitHex(source->Get(i + 2),
3248 source->Get(i + 3))) != -1 &&
3249 (lo = TwoDigitHex(source->Get(i + 4),
3250 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003251 *step = 6;
3252 return (hi << 8) + lo;
3253 } else if (character == '%' &&
3254 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003255 (lo = TwoDigitHex(source->Get(i + 1),
3256 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003257 *step = 3;
3258 return lo;
3259 } else {
3260 *step = 1;
3261 return character;
3262 }
3263}
3264
3265
3266static Object* Runtime_URIUnescape(Arguments args) {
3267 NoHandleAllocation ha;
3268 ASSERT(args.length() == 1);
3269 CONVERT_CHECKED(String, source, args[0]);
3270
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003271 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003272
3273 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003274 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003275
3276 int unescaped_length = 0;
3277 for (int i = 0; i < length; unescaped_length++) {
3278 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003279 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003281 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003282 i += step;
3283 }
3284
3285 // No length change implies no change. Return original string if no change.
3286 if (unescaped_length == length)
3287 return source;
3288
3289 Object* o = ascii ?
3290 Heap::AllocateRawAsciiString(unescaped_length) :
3291 Heap::AllocateRawTwoByteString(unescaped_length);
3292 if (o->IsFailure()) return o;
3293 String* destination = String::cast(o);
3294
3295 int dest_position = 0;
3296 for (int i = 0; i < length; dest_position++) {
3297 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003298 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003299 i += step;
3300 }
3301 return destination;
3302}
3303
3304
3305static Object* Runtime_StringParseInt(Arguments args) {
3306 NoHandleAllocation ha;
3307
3308 CONVERT_CHECKED(String, s, args[0]);
3309 CONVERT_DOUBLE_CHECKED(n, args[1]);
3310 int radix = FastD2I(n);
3311
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003312 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003313
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003314 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003315 int i;
3316
3317 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003318 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003319 if (i == len) return Heap::nan_value();
3320
3321 // Compute the sign (default to +).
3322 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003323 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003324 sign = -1;
3325 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003326 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003327 i++;
3328 }
3329
3330 // Compute the radix if 0.
3331 if (radix == 0) {
3332 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003333 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003334 radix = 8;
3335 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003336 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003337 if (c == 'x' || c == 'X') {
3338 radix = 16;
3339 i += 2;
3340 }
3341 }
3342 }
3343 } else if (radix == 16) {
3344 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003345 if (i + 1 < len && s->Get(i) == '0') {
3346 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003347 if (c == 'x' || c == 'X') i += 2;
3348 }
3349 }
3350
3351 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3352 double value;
3353 int end_index = StringToInt(s, i, radix, &value);
3354 if (end_index != i) {
3355 return Heap::NumberFromDouble(sign * value);
3356 }
3357 return Heap::nan_value();
3358}
3359
3360
3361static Object* Runtime_StringParseFloat(Arguments args) {
3362 NoHandleAllocation ha;
3363 CONVERT_CHECKED(String, str, args[0]);
3364
3365 // ECMA-262 section 15.1.2.3, empty string is NaN
3366 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3367
3368 // Create a number object from the value.
3369 return Heap::NumberFromDouble(value);
3370}
3371
3372
3373static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3374static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3375
3376
3377template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003378static Object* ConvertCaseHelper(String* s,
3379 int length,
3380 int input_string_length,
3381 unibrow::Mapping<Converter, 128>* mapping) {
3382 // We try this twice, once with the assumption that the result is no longer
3383 // than the input and, if that assumption breaks, again with the exact
3384 // length. This may not be pretty, but it is nicer than what was here before
3385 // and I hereby claim my vaffel-is.
3386 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003387 // Allocate the resulting string.
3388 //
3389 // NOTE: This assumes that the upper/lower case of an ascii
3390 // character is also ascii. This is currently the case, but it
3391 // might break in the future if we implement more context and locale
3392 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003393 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003394 ? Heap::AllocateRawAsciiString(length)
3395 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003396 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003397 String* result = String::cast(o);
3398 bool has_changed_character = false;
3399
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003400 // Convert all characters to upper case, assuming that they will fit
3401 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003402 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003403 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003404 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003405 // We can assume that the string is not empty
3406 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003407 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003408 bool has_next = buffer->has_more();
3409 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003410 int char_length = mapping->get(current, next, chars);
3411 if (char_length == 0) {
3412 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003413 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003414 i++;
3415 } else if (char_length == 1) {
3416 // Common case: converting the letter resulted in one character.
3417 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003418 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003419 has_changed_character = true;
3420 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003421 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003422 // We've assumed that the result would be as long as the
3423 // input but here is a character that converts to several
3424 // characters. No matter, we calculate the exact length
3425 // of the result and try the whole thing again.
3426 //
3427 // Note that this leaves room for optimization. We could just
3428 // memcpy what we already have to the result string. Also,
3429 // the result string is the last object allocated we could
3430 // "realloc" it and probably, in the vast majority of cases,
3431 // extend the existing string to be able to hold the full
3432 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003433 int next_length = 0;
3434 if (has_next) {
3435 next_length = mapping->get(next, 0, chars);
3436 if (next_length == 0) next_length = 1;
3437 }
3438 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003439 while (buffer->has_more()) {
3440 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003441 // NOTE: we use 0 as the next character here because, while
3442 // the next character may affect what a character converts to,
3443 // it does not in any case affect the length of what it convert
3444 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003445 int char_length = mapping->get(current, 0, chars);
3446 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003447 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003448 if (current_length > Smi::kMaxValue) {
3449 Top::context()->mark_out_of_memory();
3450 return Failure::OutOfMemoryException();
3451 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003452 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003453 // Try again with the real length.
3454 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003455 } else {
3456 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003457 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003458 i++;
3459 }
3460 has_changed_character = true;
3461 }
3462 current = next;
3463 }
3464 if (has_changed_character) {
3465 return result;
3466 } else {
3467 // If we didn't actually change anything in doing the conversion
3468 // we simple return the result and let the converted string
3469 // become garbage; there is no reason to keep two identical strings
3470 // alive.
3471 return s;
3472 }
3473}
3474
3475
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003476template <class Converter>
3477static Object* ConvertCase(Arguments args,
3478 unibrow::Mapping<Converter, 128>* mapping) {
3479 NoHandleAllocation ha;
3480
3481 CONVERT_CHECKED(String, s, args[0]);
3482 s->TryFlattenIfNotFlat();
3483
3484 int input_string_length = s->length();
3485 // Assume that the string is not empty; we need this assumption later
3486 if (input_string_length == 0) return s;
3487 int length = input_string_length;
3488
3489 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3490 if (answer->IsSmi()) {
3491 // Retry with correct length.
3492 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3493 }
3494 return answer; // This may be a failure.
3495}
3496
3497
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003498static Object* Runtime_StringToLowerCase(Arguments args) {
3499 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3500}
3501
3502
3503static Object* Runtime_StringToUpperCase(Arguments args) {
3504 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3505}
3506
3507
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003508bool Runtime::IsUpperCaseChar(uint16_t ch) {
3509 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3510 int char_length = to_upper_mapping.get(ch, 0, chars);
3511 return char_length == 0;
3512}
3513
3514
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003515static Object* Runtime_NumberToString(Arguments args) {
3516 NoHandleAllocation ha;
3517 ASSERT(args.length() == 1);
3518
3519 Object* number = args[0];
3520 RUNTIME_ASSERT(number->IsNumber());
3521
3522 Object* cached = Heap::GetNumberStringCache(number);
3523 if (cached != Heap::undefined_value()) {
3524 return cached;
3525 }
3526
3527 char arr[100];
3528 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3529 const char* str;
3530 if (number->IsSmi()) {
3531 int num = Smi::cast(number)->value();
3532 str = IntToCString(num, buffer);
3533 } else {
3534 double num = HeapNumber::cast(number)->value();
3535 str = DoubleToCString(num, buffer);
3536 }
3537 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3538
3539 if (!result->IsFailure()) {
3540 Heap::SetNumberStringCache(number, String::cast(result));
3541 }
3542 return result;
3543}
3544
3545
3546static Object* Runtime_NumberToInteger(Arguments args) {
3547 NoHandleAllocation ha;
3548 ASSERT(args.length() == 1);
3549
3550 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003551 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003552 CONVERT_DOUBLE_CHECKED(number, obj);
3553 return Heap::NumberFromDouble(DoubleToInteger(number));
3554}
3555
3556
3557static Object* Runtime_NumberToJSUint32(Arguments args) {
3558 NoHandleAllocation ha;
3559 ASSERT(args.length() == 1);
3560
3561 Object* obj = args[0];
3562 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3563 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3564 return Heap::NumberFromUint32(number);
3565}
3566
3567
3568static Object* Runtime_NumberToJSInt32(Arguments args) {
3569 NoHandleAllocation ha;
3570 ASSERT(args.length() == 1);
3571
3572 Object* obj = args[0];
3573 if (obj->IsSmi()) return obj;
3574 CONVERT_DOUBLE_CHECKED(number, obj);
3575 return Heap::NumberFromInt32(DoubleToInt32(number));
3576}
3577
3578
ager@chromium.org870a0b62008-11-04 11:43:05 +00003579// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3580// a small integer.
3581static Object* Runtime_NumberToSmi(Arguments args) {
3582 NoHandleAllocation ha;
3583 ASSERT(args.length() == 1);
3584
3585 Object* obj = args[0];
3586 if (obj->IsSmi()) {
3587 return obj;
3588 }
3589 if (obj->IsHeapNumber()) {
3590 double value = HeapNumber::cast(obj)->value();
3591 int int_value = FastD2I(value);
3592 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3593 return Smi::FromInt(int_value);
3594 }
3595 }
3596 return Heap::nan_value();
3597}
3598
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003599
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003600static Object* Runtime_NumberAdd(Arguments args) {
3601 NoHandleAllocation ha;
3602 ASSERT(args.length() == 2);
3603
3604 CONVERT_DOUBLE_CHECKED(x, args[0]);
3605 CONVERT_DOUBLE_CHECKED(y, args[1]);
3606 return Heap::AllocateHeapNumber(x + y);
3607}
3608
3609
3610static Object* Runtime_NumberSub(Arguments args) {
3611 NoHandleAllocation ha;
3612 ASSERT(args.length() == 2);
3613
3614 CONVERT_DOUBLE_CHECKED(x, args[0]);
3615 CONVERT_DOUBLE_CHECKED(y, args[1]);
3616 return Heap::AllocateHeapNumber(x - y);
3617}
3618
3619
3620static Object* Runtime_NumberMul(Arguments args) {
3621 NoHandleAllocation ha;
3622 ASSERT(args.length() == 2);
3623
3624 CONVERT_DOUBLE_CHECKED(x, args[0]);
3625 CONVERT_DOUBLE_CHECKED(y, args[1]);
3626 return Heap::AllocateHeapNumber(x * y);
3627}
3628
3629
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003630static Object* Runtime_NumberUnaryMinus(Arguments args) {
3631 NoHandleAllocation ha;
3632 ASSERT(args.length() == 1);
3633
3634 CONVERT_DOUBLE_CHECKED(x, args[0]);
3635 return Heap::AllocateHeapNumber(-x);
3636}
3637
3638
3639static Object* Runtime_NumberDiv(Arguments args) {
3640 NoHandleAllocation ha;
3641 ASSERT(args.length() == 2);
3642
3643 CONVERT_DOUBLE_CHECKED(x, args[0]);
3644 CONVERT_DOUBLE_CHECKED(y, args[1]);
3645 return Heap::NewNumberFromDouble(x / y);
3646}
3647
3648
3649static Object* Runtime_NumberMod(Arguments args) {
3650 NoHandleAllocation ha;
3651 ASSERT(args.length() == 2);
3652
3653 CONVERT_DOUBLE_CHECKED(x, args[0]);
3654 CONVERT_DOUBLE_CHECKED(y, args[1]);
3655
3656#ifdef WIN32
3657 // Workaround MS fmod bugs. ECMA-262 says:
3658 // dividend is finite and divisor is an infinity => result equals dividend
3659 // dividend is a zero and divisor is nonzero finite => result equals dividend
3660 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3661 !(x == 0 && (y != 0 && isfinite(y))))
3662#endif
3663 x = fmod(x, y);
3664 // NewNumberFromDouble may return a Smi instead of a Number object
3665 return Heap::NewNumberFromDouble(x);
3666}
3667
3668
3669static Object* Runtime_StringAdd(Arguments args) {
3670 NoHandleAllocation ha;
3671 ASSERT(args.length() == 2);
3672
3673 CONVERT_CHECKED(String, str1, args[0]);
3674 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003675 int len1 = str1->length();
3676 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003677 if (len1 == 0) return str2;
3678 if (len2 == 0) return str1;
3679 int length_sum = len1 + len2;
3680 // Make sure that an out of memory exception is thrown if the length
3681 // of the new cons string is too large to fit in a Smi.
3682 if (length_sum > Smi::kMaxValue || length_sum < 0) {
3683 Top::context()->mark_out_of_memory();
3684 return Failure::OutOfMemoryException();
3685 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003686 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003687}
3688
3689
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003690template<typename sinkchar>
3691static inline void StringBuilderConcatHelper(String* special,
3692 sinkchar* sink,
3693 FixedArray* fixed_array,
3694 int array_length) {
3695 int position = 0;
3696 for (int i = 0; i < array_length; i++) {
3697 Object* element = fixed_array->get(i);
3698 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003699 int encoded_slice = Smi::cast(element)->value();
3700 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3701 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003702 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003703 sink + position,
3704 pos,
3705 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003706 position += len;
3707 } else {
3708 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003709 int element_length = string->length();
3710 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003711 position += element_length;
3712 }
3713 }
3714}
3715
3716
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003717static Object* Runtime_StringBuilderConcat(Arguments args) {
3718 NoHandleAllocation ha;
3719 ASSERT(args.length() == 2);
3720 CONVERT_CHECKED(JSArray, array, args[0]);
3721 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003722 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 Object* smi_array_length = array->length();
3724 if (!smi_array_length->IsSmi()) {
3725 Top::context()->mark_out_of_memory();
3726 return Failure::OutOfMemoryException();
3727 }
3728 int array_length = Smi::cast(smi_array_length)->value();
3729 if (!array->HasFastElements()) {
3730 return Top::Throw(Heap::illegal_argument_symbol());
3731 }
3732 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003733 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003734 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003735 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003736
3737 if (array_length == 0) {
3738 return Heap::empty_string();
3739 } else if (array_length == 1) {
3740 Object* first = fixed_array->get(0);
3741 if (first->IsString()) return first;
3742 }
3743
ager@chromium.org5ec48922009-05-05 07:25:34 +00003744 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003745 int position = 0;
3746 for (int i = 0; i < array_length; i++) {
3747 Object* elt = fixed_array->get(i);
3748 if (elt->IsSmi()) {
3749 int len = Smi::cast(elt)->value();
3750 int pos = len >> 11;
3751 len &= 0x7ff;
3752 if (pos + len > special_length) {
3753 return Top::Throw(Heap::illegal_argument_symbol());
3754 }
3755 position += len;
3756 } else if (elt->IsString()) {
3757 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003758 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003759 if (!Smi::IsValid(element_length + position)) {
3760 Top::context()->mark_out_of_memory();
3761 return Failure::OutOfMemoryException();
3762 }
3763 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003764 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003765 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003766 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003767 } else {
3768 return Top::Throw(Heap::illegal_argument_symbol());
3769 }
3770 }
3771
3772 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003773 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003774
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003775 if (ascii) {
3776 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003777 if (object->IsFailure()) return object;
3778 SeqAsciiString* answer = SeqAsciiString::cast(object);
3779 StringBuilderConcatHelper(special,
3780 answer->GetChars(),
3781 fixed_array,
3782 array_length);
3783 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003784 } else {
3785 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003786 if (object->IsFailure()) return object;
3787 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3788 StringBuilderConcatHelper(special,
3789 answer->GetChars(),
3790 fixed_array,
3791 array_length);
3792 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003793 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003794}
3795
3796
3797static Object* Runtime_NumberOr(Arguments args) {
3798 NoHandleAllocation ha;
3799 ASSERT(args.length() == 2);
3800
3801 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3802 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3803 return Heap::NumberFromInt32(x | y);
3804}
3805
3806
3807static Object* Runtime_NumberAnd(Arguments args) {
3808 NoHandleAllocation ha;
3809 ASSERT(args.length() == 2);
3810
3811 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3812 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3813 return Heap::NumberFromInt32(x & y);
3814}
3815
3816
3817static Object* Runtime_NumberXor(Arguments args) {
3818 NoHandleAllocation ha;
3819 ASSERT(args.length() == 2);
3820
3821 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3822 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3823 return Heap::NumberFromInt32(x ^ y);
3824}
3825
3826
3827static Object* Runtime_NumberNot(Arguments args) {
3828 NoHandleAllocation ha;
3829 ASSERT(args.length() == 1);
3830
3831 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3832 return Heap::NumberFromInt32(~x);
3833}
3834
3835
3836static Object* Runtime_NumberShl(Arguments args) {
3837 NoHandleAllocation ha;
3838 ASSERT(args.length() == 2);
3839
3840 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3841 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3842 return Heap::NumberFromInt32(x << (y & 0x1f));
3843}
3844
3845
3846static Object* Runtime_NumberShr(Arguments args) {
3847 NoHandleAllocation ha;
3848 ASSERT(args.length() == 2);
3849
3850 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3851 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3852 return Heap::NumberFromUint32(x >> (y & 0x1f));
3853}
3854
3855
3856static Object* Runtime_NumberSar(Arguments args) {
3857 NoHandleAllocation ha;
3858 ASSERT(args.length() == 2);
3859
3860 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3861 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3862 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3863}
3864
3865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866static Object* Runtime_NumberEquals(Arguments args) {
3867 NoHandleAllocation ha;
3868 ASSERT(args.length() == 2);
3869
3870 CONVERT_DOUBLE_CHECKED(x, args[0]);
3871 CONVERT_DOUBLE_CHECKED(y, args[1]);
3872 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3873 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3874 if (x == y) return Smi::FromInt(EQUAL);
3875 Object* result;
3876 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3877 result = Smi::FromInt(EQUAL);
3878 } else {
3879 result = Smi::FromInt(NOT_EQUAL);
3880 }
3881 return result;
3882}
3883
3884
3885static Object* Runtime_StringEquals(Arguments args) {
3886 NoHandleAllocation ha;
3887 ASSERT(args.length() == 2);
3888
3889 CONVERT_CHECKED(String, x, args[0]);
3890 CONVERT_CHECKED(String, y, args[1]);
3891
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003892 bool not_equal = !x->Equals(y);
3893 // This is slightly convoluted because the value that signifies
3894 // equality is 0 and inequality is 1 so we have to negate the result
3895 // from String::Equals.
3896 ASSERT(not_equal == 0 || not_equal == 1);
3897 STATIC_CHECK(EQUAL == 0);
3898 STATIC_CHECK(NOT_EQUAL == 1);
3899 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003900}
3901
3902
3903static Object* Runtime_NumberCompare(Arguments args) {
3904 NoHandleAllocation ha;
3905 ASSERT(args.length() == 3);
3906
3907 CONVERT_DOUBLE_CHECKED(x, args[0]);
3908 CONVERT_DOUBLE_CHECKED(y, args[1]);
3909 if (isnan(x) || isnan(y)) return args[2];
3910 if (x == y) return Smi::FromInt(EQUAL);
3911 if (isless(x, y)) return Smi::FromInt(LESS);
3912 return Smi::FromInt(GREATER);
3913}
3914
3915
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003916// Compare two Smis as if they were converted to strings and then
3917// compared lexicographically.
3918static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3919 NoHandleAllocation ha;
3920 ASSERT(args.length() == 2);
3921
3922 // Arrays for the individual characters of the two Smis. Smis are
3923 // 31 bit integers and 10 decimal digits are therefore enough.
3924 static int x_elms[10];
3925 static int y_elms[10];
3926
3927 // Extract the integer values from the Smis.
3928 CONVERT_CHECKED(Smi, x, args[0]);
3929 CONVERT_CHECKED(Smi, y, args[1]);
3930 int x_value = x->value();
3931 int y_value = y->value();
3932
3933 // If the integers are equal so are the string representations.
3934 if (x_value == y_value) return Smi::FromInt(EQUAL);
3935
3936 // If one of the integers are zero the normal integer order is the
3937 // same as the lexicographic order of the string representations.
3938 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3939
ager@chromium.org32912102009-01-16 10:38:43 +00003940 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003941 // smallest because the char code of '-' is less than the char code
3942 // of any digit. Otherwise, we make both values positive.
3943 if (x_value < 0 || y_value < 0) {
3944 if (y_value >= 0) return Smi::FromInt(LESS);
3945 if (x_value >= 0) return Smi::FromInt(GREATER);
3946 x_value = -x_value;
3947 y_value = -y_value;
3948 }
3949
3950 // Convert the integers to arrays of their decimal digits.
3951 int x_index = 0;
3952 int y_index = 0;
3953 while (x_value > 0) {
3954 x_elms[x_index++] = x_value % 10;
3955 x_value /= 10;
3956 }
3957 while (y_value > 0) {
3958 y_elms[y_index++] = y_value % 10;
3959 y_value /= 10;
3960 }
3961
3962 // Loop through the arrays of decimal digits finding the first place
3963 // where they differ.
3964 while (--x_index >= 0 && --y_index >= 0) {
3965 int diff = x_elms[x_index] - y_elms[y_index];
3966 if (diff != 0) return Smi::FromInt(diff);
3967 }
3968
3969 // If one array is a suffix of the other array, the longest array is
3970 // the representation of the largest of the Smis in the
3971 // lexicographic ordering.
3972 return Smi::FromInt(x_index - y_index);
3973}
3974
3975
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003976static Object* Runtime_StringCompare(Arguments args) {
3977 NoHandleAllocation ha;
3978 ASSERT(args.length() == 2);
3979
3980 CONVERT_CHECKED(String, x, args[0]);
3981 CONVERT_CHECKED(String, y, args[1]);
3982
3983 // A few fast case tests before we flatten.
3984 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003985 if (y->length() == 0) {
3986 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003987 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003988 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003989 return Smi::FromInt(LESS);
3990 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003991
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003992 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003993 if (d < 0) return Smi::FromInt(LESS);
3994 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003995
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003996 x->TryFlattenIfNotFlat();
3997 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003998
3999 static StringInputBuffer bufx;
4000 static StringInputBuffer bufy;
4001 bufx.Reset(x);
4002 bufy.Reset(y);
4003 while (bufx.has_more() && bufy.has_more()) {
4004 int d = bufx.GetNext() - bufy.GetNext();
4005 if (d < 0) return Smi::FromInt(LESS);
4006 else if (d > 0) return Smi::FromInt(GREATER);
4007 }
4008
4009 // x is (non-trivial) prefix of y:
4010 if (bufy.has_more()) return Smi::FromInt(LESS);
4011 // y is prefix of x:
4012 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4013}
4014
4015
4016static Object* Runtime_Math_abs(Arguments args) {
4017 NoHandleAllocation ha;
4018 ASSERT(args.length() == 1);
4019
4020 CONVERT_DOUBLE_CHECKED(x, args[0]);
4021 return Heap::AllocateHeapNumber(fabs(x));
4022}
4023
4024
4025static Object* Runtime_Math_acos(Arguments args) {
4026 NoHandleAllocation ha;
4027 ASSERT(args.length() == 1);
4028
4029 CONVERT_DOUBLE_CHECKED(x, args[0]);
4030 return Heap::AllocateHeapNumber(acos(x));
4031}
4032
4033
4034static Object* Runtime_Math_asin(Arguments args) {
4035 NoHandleAllocation ha;
4036 ASSERT(args.length() == 1);
4037
4038 CONVERT_DOUBLE_CHECKED(x, args[0]);
4039 return Heap::AllocateHeapNumber(asin(x));
4040}
4041
4042
4043static Object* Runtime_Math_atan(Arguments args) {
4044 NoHandleAllocation ha;
4045 ASSERT(args.length() == 1);
4046
4047 CONVERT_DOUBLE_CHECKED(x, args[0]);
4048 return Heap::AllocateHeapNumber(atan(x));
4049}
4050
4051
4052static Object* Runtime_Math_atan2(Arguments args) {
4053 NoHandleAllocation ha;
4054 ASSERT(args.length() == 2);
4055
4056 CONVERT_DOUBLE_CHECKED(x, args[0]);
4057 CONVERT_DOUBLE_CHECKED(y, args[1]);
4058 double result;
4059 if (isinf(x) && isinf(y)) {
4060 // Make sure that the result in case of two infinite arguments
4061 // is a multiple of Pi / 4. The sign of the result is determined
4062 // by the first argument (x) and the sign of the second argument
4063 // determines the multiplier: one or three.
4064 static double kPiDividedBy4 = 0.78539816339744830962;
4065 int multiplier = (x < 0) ? -1 : 1;
4066 if (y < 0) multiplier *= 3;
4067 result = multiplier * kPiDividedBy4;
4068 } else {
4069 result = atan2(x, y);
4070 }
4071 return Heap::AllocateHeapNumber(result);
4072}
4073
4074
4075static Object* Runtime_Math_ceil(Arguments args) {
4076 NoHandleAllocation ha;
4077 ASSERT(args.length() == 1);
4078
4079 CONVERT_DOUBLE_CHECKED(x, args[0]);
4080 return Heap::NumberFromDouble(ceiling(x));
4081}
4082
4083
4084static Object* Runtime_Math_cos(Arguments args) {
4085 NoHandleAllocation ha;
4086 ASSERT(args.length() == 1);
4087
4088 CONVERT_DOUBLE_CHECKED(x, args[0]);
4089 return Heap::AllocateHeapNumber(cos(x));
4090}
4091
4092
4093static Object* Runtime_Math_exp(Arguments args) {
4094 NoHandleAllocation ha;
4095 ASSERT(args.length() == 1);
4096
4097 CONVERT_DOUBLE_CHECKED(x, args[0]);
4098 return Heap::AllocateHeapNumber(exp(x));
4099}
4100
4101
4102static Object* Runtime_Math_floor(Arguments args) {
4103 NoHandleAllocation ha;
4104 ASSERT(args.length() == 1);
4105
4106 CONVERT_DOUBLE_CHECKED(x, args[0]);
4107 return Heap::NumberFromDouble(floor(x));
4108}
4109
4110
4111static Object* Runtime_Math_log(Arguments args) {
4112 NoHandleAllocation ha;
4113 ASSERT(args.length() == 1);
4114
4115 CONVERT_DOUBLE_CHECKED(x, args[0]);
4116 return Heap::AllocateHeapNumber(log(x));
4117}
4118
4119
4120static Object* Runtime_Math_pow(Arguments args) {
4121 NoHandleAllocation ha;
4122 ASSERT(args.length() == 2);
4123
4124 CONVERT_DOUBLE_CHECKED(x, args[0]);
4125 CONVERT_DOUBLE_CHECKED(y, args[1]);
4126 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4127 return Heap::nan_value();
4128 } else if (y == 0) {
4129 return Smi::FromInt(1);
4130 } else {
4131 return Heap::AllocateHeapNumber(pow(x, y));
4132 }
4133}
4134
4135// Returns a number value with positive sign, greater than or equal to
4136// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00004137static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004138 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004139 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004140
4141 // To get much better precision, we combine the results of two
4142 // invocations of random(). The result is computed by normalizing a
4143 // double in the range [0, RAND_MAX + 1) obtained by adding the
4144 // high-order bits in the range [0, RAND_MAX] with the low-order
4145 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004146 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004147 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004148 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004149 ASSERT(result >= 0 && result < 1);
4150 return Heap::AllocateHeapNumber(result);
4151}
4152
4153
4154static Object* Runtime_Math_round(Arguments args) {
4155 NoHandleAllocation ha;
4156 ASSERT(args.length() == 1);
4157
4158 CONVERT_DOUBLE_CHECKED(x, args[0]);
4159 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4160 return Heap::NumberFromDouble(floor(x + 0.5));
4161}
4162
4163
4164static Object* Runtime_Math_sin(Arguments args) {
4165 NoHandleAllocation ha;
4166 ASSERT(args.length() == 1);
4167
4168 CONVERT_DOUBLE_CHECKED(x, args[0]);
4169 return Heap::AllocateHeapNumber(sin(x));
4170}
4171
4172
4173static Object* Runtime_Math_sqrt(Arguments args) {
4174 NoHandleAllocation ha;
4175 ASSERT(args.length() == 1);
4176
4177 CONVERT_DOUBLE_CHECKED(x, args[0]);
4178 return Heap::AllocateHeapNumber(sqrt(x));
4179}
4180
4181
4182static Object* Runtime_Math_tan(Arguments args) {
4183 NoHandleAllocation ha;
4184 ASSERT(args.length() == 1);
4185
4186 CONVERT_DOUBLE_CHECKED(x, args[0]);
4187 return Heap::AllocateHeapNumber(tan(x));
4188}
4189
4190
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004191// The NewArguments function is only used when constructing the
4192// arguments array when calling non-functions from JavaScript in
4193// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004194static Object* Runtime_NewArguments(Arguments args) {
4195 NoHandleAllocation ha;
4196 ASSERT(args.length() == 1);
4197
4198 // ECMA-262, 3rd., 10.1.8, p.39
4199 CONVERT_CHECKED(JSFunction, callee, args[0]);
4200
4201 // Compute the frame holding the arguments.
4202 JavaScriptFrameIterator it;
4203 it.AdvanceToArgumentsFrame();
4204 JavaScriptFrame* frame = it.frame();
4205
4206 const int length = frame->GetProvidedParametersCount();
4207 Object* result = Heap::AllocateArgumentsObject(callee, length);
4208 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004209 if (length > 0) {
4210 Object* obj = Heap::AllocateFixedArray(length);
4211 if (obj->IsFailure()) return obj;
4212 FixedArray* array = FixedArray::cast(obj);
4213 ASSERT(array->length() == length);
4214 WriteBarrierMode mode = array->GetWriteBarrierMode();
4215 for (int i = 0; i < length; i++) {
4216 array->set(i, frame->GetParameter(i), mode);
4217 }
4218 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004219 }
4220 return result;
4221}
4222
4223
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004224static Object* Runtime_NewArgumentsFast(Arguments args) {
4225 NoHandleAllocation ha;
4226 ASSERT(args.length() == 3);
4227
4228 JSFunction* callee = JSFunction::cast(args[0]);
4229 Object** parameters = reinterpret_cast<Object**>(args[1]);
4230 const int length = Smi::cast(args[2])->value();
4231
4232 Object* result = Heap::AllocateArgumentsObject(callee, length);
4233 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004234 ASSERT(Heap::InNewSpace(result));
4235
4236 // Allocate the elements if needed.
4237 if (length > 0) {
4238 // Allocate the fixed array.
4239 Object* obj = Heap::AllocateRawFixedArray(length);
4240 if (obj->IsFailure()) return obj;
4241 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4242 FixedArray* array = FixedArray::cast(obj);
4243 array->set_length(length);
4244 WriteBarrierMode mode = array->GetWriteBarrierMode();
4245 for (int i = 0; i < length; i++) {
4246 array->set(i, *--parameters, mode);
4247 }
4248 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4249 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004250 }
4251 return result;
4252}
4253
4254
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004255static Object* Runtime_NewClosure(Arguments args) {
4256 HandleScope scope;
4257 ASSERT(args.length() == 2);
4258 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4259 CONVERT_ARG_CHECKED(Context, context, 1);
4260
4261 Handle<JSFunction> result =
4262 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4263 return *result;
4264}
4265
4266
4267static Object* Runtime_NewObject(Arguments args) {
4268 NoHandleAllocation ha;
4269 ASSERT(args.length() == 1);
4270
4271 Object* constructor = args[0];
4272 if (constructor->IsJSFunction()) {
4273 JSFunction* function = JSFunction::cast(constructor);
4274
ager@chromium.org32912102009-01-16 10:38:43 +00004275 // Handle stepping into constructors if step into is active.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004276#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004277 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004278 HandleScope scope;
4279 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004280 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004281#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004282
4283 if (function->has_initial_map() &&
4284 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
4285 // The 'Function' function ignores the receiver object when
4286 // called using 'new' and creates a new JSFunction object that
4287 // is returned. The receiver object is only used for error
4288 // reporting if an error occurs when constructing the new
4289 // JSFunction. AllocateJSObject should not be used to allocate
4290 // JSFunctions since it does not properly initialize the shared
4291 // part of the function. Since the receiver is ignored anyway,
4292 // we use the global object as the receiver instead of a new
4293 // JSFunction object. This way, errors are reported the same
4294 // way whether or not 'Function' is called using 'new'.
4295 return Top::context()->global();
4296 }
4297 return Heap::AllocateJSObject(function);
4298 }
4299
4300 HandleScope scope;
4301 Handle<Object> cons(constructor);
4302 // The constructor is not a function; throw a type error.
4303 Handle<Object> type_error =
4304 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
4305 return Top::Throw(*type_error);
4306}
4307
4308
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004309static Object* Runtime_LazyCompile(Arguments args) {
4310 HandleScope scope;
4311 ASSERT(args.length() == 1);
4312
4313 Handle<JSFunction> function = args.at<JSFunction>(0);
4314#ifdef DEBUG
4315 if (FLAG_trace_lazy) {
4316 PrintF("[lazy: ");
4317 function->shared()->name()->Print();
4318 PrintF("]\n");
4319 }
4320#endif
4321
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004322 // Compile the target function. Here we compile using CompileLazyInLoop in
4323 // order to get the optimized version. This helps code like delta-blue
4324 // that calls performance-critical routines through constructors. A
4325 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4326 // direct call. Since the in-loop tracking takes place through CallICs
4327 // this means that things called through constructors are never known to
4328 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004329 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004330 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004331 return Failure::Exception();
4332 }
4333
4334 return function->code();
4335}
4336
4337
4338static Object* Runtime_GetCalledFunction(Arguments args) {
4339 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004340 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004341 StackFrameIterator it;
4342 // Get past the JS-to-C exit frame.
4343 ASSERT(it.frame()->is_exit());
4344 it.Advance();
4345 // Get past the CALL_NON_FUNCTION activation frame.
4346 ASSERT(it.frame()->is_java_script());
4347 it.Advance();
4348 // Argument adaptor frames do not copy the function; we have to skip
4349 // past them to get to the real calling frame.
4350 if (it.frame()->is_arguments_adaptor()) it.Advance();
4351 // Get the function from the top of the expression stack of the
4352 // calling frame.
4353 StandardFrame* frame = StandardFrame::cast(it.frame());
4354 int index = frame->ComputeExpressionsCount() - 1;
4355 Object* result = frame->GetExpression(index);
4356 return result;
4357}
4358
4359
4360static Object* Runtime_GetFunctionDelegate(Arguments args) {
4361 HandleScope scope;
4362 ASSERT(args.length() == 1);
4363 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4364 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4365}
4366
4367
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004368static Object* Runtime_GetConstructorDelegate(Arguments args) {
4369 HandleScope scope;
4370 ASSERT(args.length() == 1);
4371 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4372 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4373}
4374
4375
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004376static Object* Runtime_NewContext(Arguments args) {
4377 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004378 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004379
kasper.lund7276f142008-07-30 08:49:36 +00004380 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004381 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4382 Object* result = Heap::AllocateFunctionContext(length, function);
4383 if (result->IsFailure()) return result;
4384
4385 Top::set_context(Context::cast(result));
4386
kasper.lund7276f142008-07-30 08:49:36 +00004387 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004388}
4389
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004390static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004391 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004392 Object* js_object = object;
4393 if (!js_object->IsJSObject()) {
4394 js_object = js_object->ToObject();
4395 if (js_object->IsFailure()) {
4396 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004397 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004398 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004399 Handle<Object> result =
4400 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4401 return Top::Throw(*result);
4402 }
4403 }
4404
4405 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004406 Heap::AllocateWithContext(Top::context(),
4407 JSObject::cast(js_object),
4408 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004409 if (result->IsFailure()) return result;
4410
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004411 Context* context = Context::cast(result);
4412 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004413
kasper.lund7276f142008-07-30 08:49:36 +00004414 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004415}
4416
4417
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004418static Object* Runtime_PushContext(Arguments args) {
4419 NoHandleAllocation ha;
4420 ASSERT(args.length() == 1);
4421 return PushContextHelper(args[0], false);
4422}
4423
4424
4425static Object* Runtime_PushCatchContext(Arguments args) {
4426 NoHandleAllocation ha;
4427 ASSERT(args.length() == 1);
4428 return PushContextHelper(args[0], true);
4429}
4430
4431
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004432static Object* Runtime_LookupContext(Arguments args) {
4433 HandleScope scope;
4434 ASSERT(args.length() == 2);
4435
4436 CONVERT_ARG_CHECKED(Context, context, 0);
4437 CONVERT_ARG_CHECKED(String, name, 1);
4438
4439 int index;
4440 PropertyAttributes attributes;
4441 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004442 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004443 context->Lookup(name, flags, &index, &attributes);
4444
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004445 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004446 ASSERT(holder->IsJSObject());
4447 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004448 }
4449
4450 // No intermediate context found. Use global object by default.
4451 return Top::context()->global();
4452}
4453
4454
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004455// A mechanism to return pairs of Object*'s. This is somewhat
4456// compiler-dependent as it assumes that a 64-bit value (a long long)
4457// is returned via two registers (edx:eax on ia32). Both the ia32 and
4458// arm platform support this; it is mostly an issue of "coaxing" the
4459// compiler to do the right thing.
4460//
4461// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004462// TODO(x64): Definitely!
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004463typedef uint64_t ObjectPair;
4464static inline ObjectPair MakePair(Object* x, Object* y) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004465#if V8_HOST_ARCH_64_BIT
4466 UNIMPLEMENTED();
4467 return 0;
4468#else
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004469 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004470 (reinterpret_cast<ObjectPair>(y) << 32);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004471#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004472}
4473
4474
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004475static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004476 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4477 USE(attributes);
4478 return x->IsTheHole() ? Heap::undefined_value() : x;
4479}
4480
4481
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004482static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4483 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004484 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004485 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004486 JSFunction* context_extension_function =
4487 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004488 // If the holder isn't a context extension object, we just return it
4489 // as the receiver. This allows arguments objects to be used as
4490 // receivers, but only if they are put in the context scope chain
4491 // explicitly via a with-statement.
4492 Object* constructor = holder->map()->constructor();
4493 if (constructor != context_extension_function) return holder;
4494 // Fall back to using the global object as the receiver if the
4495 // property turns out to be a local variable allocated in a context
4496 // extension object - introduced via eval.
4497 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004498}
4499
4500
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004501static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004502 HandleScope scope;
4503 ASSERT(args.length() == 2);
4504
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004505 if (!args[0]->IsContext() || !args[1]->IsString()) {
4506 return MakePair(IllegalOperation(), NULL);
4507 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004508 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004509 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004510
4511 int index;
4512 PropertyAttributes attributes;
4513 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004514 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004515 context->Lookup(name, flags, &index, &attributes);
4516
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004517 // If the index is non-negative, the slot has been found in a local
4518 // variable or a parameter. Read it from the context object or the
4519 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004520 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004521 // If the "property" we were looking for is a local variable or an
4522 // argument in a context, the receiver is the global object; see
4523 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4524 JSObject* receiver = Top::context()->global()->global_receiver();
4525 Object* value = (holder->IsContext())
4526 ? Context::cast(*holder)->get(index)
4527 : JSObject::cast(*holder)->GetElement(index);
4528 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004529 }
4530
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004531 // If the holder is found, we read the property from it.
4532 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004533 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004534 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004535 JSObject* receiver;
4536 if (object->IsGlobalObject()) {
4537 receiver = GlobalObject::cast(object)->global_receiver();
4538 } else if (context->is_exception_holder(*holder)) {
4539 receiver = Top::context()->global()->global_receiver();
4540 } else {
4541 receiver = ComputeReceiverForNonGlobal(object);
4542 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004543 // No need to unhole the value here. This is taken care of by the
4544 // GetProperty function.
4545 Object* value = object->GetProperty(*name);
4546 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004547 }
4548
4549 if (throw_error) {
4550 // The property doesn't exist - throw exception.
4551 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004552 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004553 return MakePair(Top::Throw(*reference_error), NULL);
4554 } else {
4555 // The property doesn't exist - return undefined
4556 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4557 }
4558}
4559
4560
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004561static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004562 return LoadContextSlotHelper(args, true);
4563}
4564
4565
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004566static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004567 return LoadContextSlotHelper(args, false);
4568}
4569
4570
4571static Object* Runtime_StoreContextSlot(Arguments args) {
4572 HandleScope scope;
4573 ASSERT(args.length() == 3);
4574
4575 Handle<Object> value(args[0]);
4576 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004577 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004578
4579 int index;
4580 PropertyAttributes attributes;
4581 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004582 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004583 context->Lookup(name, flags, &index, &attributes);
4584
4585 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004586 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004587 // Ignore if read_only variable.
4588 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004589 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004590 }
4591 } else {
4592 ASSERT((attributes & READ_ONLY) == 0);
4593 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004594 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004595 USE(result);
4596 ASSERT(!result->IsFailure());
4597 }
4598 return *value;
4599 }
4600
4601 // Slow case: The property is not in a FixedArray context.
4602 // It is either in an JSObject extension context or it was not found.
4603 Handle<JSObject> context_ext;
4604
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004605 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004606 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004607 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004608 } else {
4609 // The property was not found. It needs to be stored in the global context.
4610 ASSERT(attributes == ABSENT);
4611 attributes = NONE;
4612 context_ext = Handle<JSObject>(Top::context()->global());
4613 }
4614
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004615 // Set the property, but ignore if read_only variable on the context
4616 // extension object itself.
4617 if ((attributes & READ_ONLY) == 0 ||
4618 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004619 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4620 if (set.is_null()) {
4621 // Failure::Exception is converted to a null handle in the
4622 // handle-based methods such as SetProperty. We therefore need
4623 // to convert null handles back to exceptions.
4624 ASSERT(Top::has_pending_exception());
4625 return Failure::Exception();
4626 }
4627 }
4628 return *value;
4629}
4630
4631
4632static Object* Runtime_Throw(Arguments args) {
4633 HandleScope scope;
4634 ASSERT(args.length() == 1);
4635
4636 return Top::Throw(args[0]);
4637}
4638
4639
4640static Object* Runtime_ReThrow(Arguments args) {
4641 HandleScope scope;
4642 ASSERT(args.length() == 1);
4643
4644 return Top::ReThrow(args[0]);
4645}
4646
4647
4648static Object* Runtime_ThrowReferenceError(Arguments args) {
4649 HandleScope scope;
4650 ASSERT(args.length() == 1);
4651
4652 Handle<Object> name(args[0]);
4653 Handle<Object> reference_error =
4654 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4655 return Top::Throw(*reference_error);
4656}
4657
4658
4659static Object* Runtime_StackOverflow(Arguments args) {
4660 NoHandleAllocation na;
4661 return Top::StackOverflow();
4662}
4663
4664
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004665static Object* Runtime_StackGuard(Arguments args) {
4666 ASSERT(args.length() == 1);
4667
4668 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004669 if (StackGuard::IsStackOverflow()) {
4670 return Runtime_StackOverflow(args);
4671 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004672
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004673 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004674}
4675
4676
4677// NOTE: These PrintXXX functions are defined for all builds (not just
4678// DEBUG builds) because we may want to be able to trace function
4679// calls in all modes.
4680static void PrintString(String* str) {
4681 // not uncommon to have empty strings
4682 if (str->length() > 0) {
4683 SmartPointer<char> s =
4684 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4685 PrintF("%s", *s);
4686 }
4687}
4688
4689
4690static void PrintObject(Object* obj) {
4691 if (obj->IsSmi()) {
4692 PrintF("%d", Smi::cast(obj)->value());
4693 } else if (obj->IsString() || obj->IsSymbol()) {
4694 PrintString(String::cast(obj));
4695 } else if (obj->IsNumber()) {
4696 PrintF("%g", obj->Number());
4697 } else if (obj->IsFailure()) {
4698 PrintF("<failure>");
4699 } else if (obj->IsUndefined()) {
4700 PrintF("<undefined>");
4701 } else if (obj->IsNull()) {
4702 PrintF("<null>");
4703 } else if (obj->IsTrue()) {
4704 PrintF("<true>");
4705 } else if (obj->IsFalse()) {
4706 PrintF("<false>");
4707 } else {
4708 PrintF("%p", obj);
4709 }
4710}
4711
4712
4713static int StackSize() {
4714 int n = 0;
4715 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4716 return n;
4717}
4718
4719
4720static void PrintTransition(Object* result) {
4721 // indentation
4722 { const int nmax = 80;
4723 int n = StackSize();
4724 if (n <= nmax)
4725 PrintF("%4d:%*s", n, n, "");
4726 else
4727 PrintF("%4d:%*s", n, nmax, "...");
4728 }
4729
4730 if (result == NULL) {
4731 // constructor calls
4732 JavaScriptFrameIterator it;
4733 JavaScriptFrame* frame = it.frame();
4734 if (frame->IsConstructor()) PrintF("new ");
4735 // function name
4736 Object* fun = frame->function();
4737 if (fun->IsJSFunction()) {
4738 PrintObject(JSFunction::cast(fun)->shared()->name());
4739 } else {
4740 PrintObject(fun);
4741 }
4742 // function arguments
4743 // (we are intentionally only printing the actually
4744 // supplied parameters, not all parameters required)
4745 PrintF("(this=");
4746 PrintObject(frame->receiver());
4747 const int length = frame->GetProvidedParametersCount();
4748 for (int i = 0; i < length; i++) {
4749 PrintF(", ");
4750 PrintObject(frame->GetParameter(i));
4751 }
4752 PrintF(") {\n");
4753
4754 } else {
4755 // function result
4756 PrintF("} -> ");
4757 PrintObject(result);
4758 PrintF("\n");
4759 }
4760}
4761
4762
4763static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004764 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 NoHandleAllocation ha;
4766 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004767 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004768}
4769
4770
4771static Object* Runtime_TraceExit(Arguments args) {
4772 NoHandleAllocation ha;
4773 PrintTransition(args[0]);
4774 return args[0]; // return TOS
4775}
4776
4777
4778static Object* Runtime_DebugPrint(Arguments args) {
4779 NoHandleAllocation ha;
4780 ASSERT(args.length() == 1);
4781
4782#ifdef DEBUG
4783 if (args[0]->IsString()) {
4784 // If we have a string, assume it's a code "marker"
4785 // and print some interesting cpu debugging info.
4786 JavaScriptFrameIterator it;
4787 JavaScriptFrame* frame = it.frame();
4788 PrintF("fp = %p, sp = %p, pp = %p: ",
4789 frame->fp(), frame->sp(), frame->pp());
4790 } else {
4791 PrintF("DebugPrint: ");
4792 }
4793 args[0]->Print();
4794#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004795 // ShortPrint is available in release mode. Print is not.
4796 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004797#endif
4798 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004799 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004800
4801 return args[0]; // return TOS
4802}
4803
4804
4805static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004806 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004807 NoHandleAllocation ha;
4808 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004809 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004810}
4811
4812
mads.s.ager31e71382008-08-13 09:32:07 +00004813static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004814 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004815 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004816
4817 // According to ECMA-262, section 15.9.1, page 117, the precision of
4818 // the number in a Date object representing a particular instant in
4819 // time is milliseconds. Therefore, we floor the result of getting
4820 // the OS time.
4821 double millis = floor(OS::TimeCurrentMillis());
4822 return Heap::NumberFromDouble(millis);
4823}
4824
4825
4826static Object* Runtime_DateParseString(Arguments args) {
4827 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004828 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004830 CONVERT_ARG_CHECKED(String, str, 0);
4831 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004832
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004833 CONVERT_ARG_CHECKED(JSArray, output, 1);
4834 RUNTIME_ASSERT(output->HasFastElements());
4835
4836 AssertNoAllocation no_allocation;
4837
4838 FixedArray* output_array = output->elements();
4839 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4840 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004841 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004842 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004843 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004844 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004845 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4846 }
4847
4848 if (result) {
4849 return *output;
4850 } else {
4851 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852 }
4853}
4854
4855
4856static Object* Runtime_DateLocalTimezone(Arguments args) {
4857 NoHandleAllocation ha;
4858 ASSERT(args.length() == 1);
4859
4860 CONVERT_DOUBLE_CHECKED(x, args[0]);
4861 char* zone = OS::LocalTimezone(x);
4862 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4863}
4864
4865
4866static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4867 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004868 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004869
4870 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4871}
4872
4873
4874static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4875 NoHandleAllocation ha;
4876 ASSERT(args.length() == 1);
4877
4878 CONVERT_DOUBLE_CHECKED(x, args[0]);
4879 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4880}
4881
4882
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883static Object* Runtime_NumberIsFinite(Arguments args) {
4884 NoHandleAllocation ha;
4885 ASSERT(args.length() == 1);
4886
4887 CONVERT_DOUBLE_CHECKED(value, args[0]);
4888 Object* result;
4889 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4890 result = Heap::false_value();
4891 } else {
4892 result = Heap::true_value();
4893 }
4894 return result;
4895}
4896
4897
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004898static Object* Runtime_GlobalReceiver(Arguments args) {
4899 ASSERT(args.length() == 1);
4900 Object* global = args[0];
4901 if (!global->IsJSGlobalObject()) return Heap::null_value();
4902 return JSGlobalObject::cast(global)->global_receiver();
4903}
4904
4905
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004906static Object* Runtime_CompileString(Arguments args) {
4907 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004908 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004909 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004910 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004911
ager@chromium.org381abbb2009-02-25 13:23:22 +00004912 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004913 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004914 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4915 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004916 true,
4917 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004918 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004919 Handle<JSFunction> fun =
4920 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4921 return *fun;
4922}
4923
4924
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004925static Handle<JSFunction> GetBuiltinFunction(String* name) {
4926 LookupResult result;
4927 Top::global_context()->builtins()->LocalLookup(name, &result);
4928 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4929}
4930
4931
4932static Object* CompileDirectEval(Handle<String> source) {
4933 // Compute the eval context.
4934 HandleScope scope;
4935 StackFrameLocator locator;
4936 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4937 Handle<Context> context(Context::cast(frame->context()));
4938 bool is_global = context->IsGlobalContext();
4939
ager@chromium.org381abbb2009-02-25 13:23:22 +00004940 // Compile source string in the current context.
4941 Handle<JSFunction> boilerplate =
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004942 Compiler::CompileEval(source, context, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004943 if (boilerplate.is_null()) return Failure::Exception();
4944 Handle<JSFunction> fun =
4945 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4946 return *fun;
4947}
4948
4949
4950static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4951 ASSERT(args.length() == 2);
4952
4953 HandleScope scope;
4954
4955 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4956
4957 Handle<Object> receiver;
4958
4959 // Find where the 'eval' symbol is bound. It is unaliased only if
4960 // it is bound in the global context.
4961 StackFrameLocator locator;
4962 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4963 Handle<Context> context(Context::cast(frame->context()));
4964 int index;
4965 PropertyAttributes attributes;
4966 while (!context.is_null()) {
4967 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4968 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00004969 // Stop search when eval is found or when the global context is
4970 // reached.
4971 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004972 if (context->is_function_context()) {
4973 context = Handle<Context>(Context::cast(context->closure()->context()));
4974 } else {
4975 context = Handle<Context>(context->previous());
4976 }
4977 }
4978
iposva@chromium.org245aa852009-02-10 00:49:54 +00004979 // If eval could not be resolved, it has been deleted and we need to
4980 // throw a reference error.
4981 if (attributes == ABSENT) {
4982 Handle<Object> name = Factory::eval_symbol();
4983 Handle<Object> reference_error =
4984 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4985 return Top::Throw(*reference_error);
4986 }
4987
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004988 if (context->IsGlobalContext()) {
4989 // 'eval' is bound in the global context, but it may have been overwritten.
4990 // Compare it to the builtin 'GlobalEval' function to make sure.
4991 Handle<JSFunction> global_eval =
4992 GetBuiltinFunction(Heap::global_eval_symbol());
4993 if (global_eval.is_identical_to(callee)) {
4994 // A direct eval call.
4995 if (args[1]->IsString()) {
4996 CONVERT_ARG_CHECKED(String, source, 1);
4997 // A normal eval call on a string. Compile it and return the
4998 // compiled function bound in the local context.
4999 Object* compiled_source = CompileDirectEval(source);
5000 if (compiled_source->IsFailure()) return compiled_source;
5001 receiver = Handle<Object>(frame->receiver());
5002 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5003 } else {
5004 // An eval call that is not called on a string. Global eval
5005 // deals better with this.
5006 receiver = Handle<Object>(Top::global_context()->global());
5007 }
5008 } else {
5009 // 'eval' is overwritten. Just call the function with the given arguments.
5010 receiver = Handle<Object>(Top::global_context()->global());
5011 }
5012 } else {
5013 // 'eval' is not bound in the global context. Just call the function
5014 // with the given arguments. This is not necessarily the global eval.
5015 if (receiver->IsContext()) {
5016 context = Handle<Context>::cast(receiver);
5017 receiver = Handle<Object>(context->get(index));
5018 }
5019 }
5020
5021 Handle<FixedArray> call = Factory::NewFixedArray(2);
5022 call->set(0, *callee);
5023 call->set(1, *receiver);
5024 return *call;
5025}
5026
5027
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005028static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5029 // This utility adjusts the property attributes for newly created Function
5030 // object ("new Function(...)") by changing the map.
5031 // All it does is changing the prototype property to enumerable
5032 // as specified in ECMA262, 15.3.5.2.
5033 HandleScope scope;
5034 ASSERT(args.length() == 1);
5035 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5036 ASSERT(func->map()->instance_type() ==
5037 Top::function_instance_map()->instance_type());
5038 ASSERT(func->map()->instance_size() ==
5039 Top::function_instance_map()->instance_size());
5040 func->set_map(*Top::function_instance_map());
5041 return *func;
5042}
5043
5044
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005045// Push an array unto an array of arrays if it is not already in the
5046// array. Returns true if the element was pushed on the stack and
5047// false otherwise.
5048static Object* Runtime_PushIfAbsent(Arguments args) {
5049 ASSERT(args.length() == 2);
5050 CONVERT_CHECKED(JSArray, array, args[0]);
5051 CONVERT_CHECKED(JSArray, element, args[1]);
5052 RUNTIME_ASSERT(array->HasFastElements());
5053 int length = Smi::cast(array->length())->value();
5054 FixedArray* elements = FixedArray::cast(array->elements());
5055 for (int i = 0; i < length; i++) {
5056 if (elements->get(i) == element) return Heap::false_value();
5057 }
5058 Object* obj = array->SetFastElement(length, element);
5059 if (obj->IsFailure()) return obj;
5060 return Heap::true_value();
5061}
5062
5063
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005064/**
5065 * A simple visitor visits every element of Array's.
5066 * The backend storage can be a fixed array for fast elements case,
5067 * or a dictionary for sparse array. Since Dictionary is a subtype
5068 * of FixedArray, the class can be used by both fast and slow cases.
5069 * The second parameter of the constructor, fast_elements, specifies
5070 * whether the storage is a FixedArray or Dictionary.
5071 *
5072 * An index limit is used to deal with the situation that a result array
5073 * length overflows 32-bit non-negative integer.
5074 */
5075class ArrayConcatVisitor {
5076 public:
5077 ArrayConcatVisitor(Handle<FixedArray> storage,
5078 uint32_t index_limit,
5079 bool fast_elements) :
5080 storage_(storage), index_limit_(index_limit),
5081 fast_elements_(fast_elements), index_offset_(0) { }
5082
5083 void visit(uint32_t i, Handle<Object> elm) {
5084 uint32_t index = i + index_offset_;
5085 if (index >= index_limit_) return;
5086
5087 if (fast_elements_) {
5088 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5089 storage_->set(index, *elm);
5090
5091 } else {
5092 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5093 Handle<Dictionary> result =
5094 Factory::DictionaryAtNumberPut(dict, index, elm);
5095 if (!result.is_identical_to(dict))
5096 storage_ = result;
5097 }
5098 }
5099
5100 void increase_index_offset(uint32_t delta) {
5101 index_offset_ += delta;
5102 }
5103
5104 private:
5105 Handle<FixedArray> storage_;
5106 uint32_t index_limit_;
5107 bool fast_elements_;
5108 uint32_t index_offset_;
5109};
5110
5111
5112/**
5113 * A helper function that visits elements of a JSObject. Only elements
5114 * whose index between 0 and range (exclusive) are visited.
5115 *
5116 * If the third parameter, visitor, is not NULL, the visitor is called
5117 * with parameters, 'visitor_index_offset + element index' and the element.
5118 *
5119 * It returns the number of visisted elements.
5120 */
5121static uint32_t IterateElements(Handle<JSObject> receiver,
5122 uint32_t range,
5123 ArrayConcatVisitor* visitor) {
5124 uint32_t num_of_elements = 0;
5125
5126 if (receiver->HasFastElements()) {
5127 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5128 uint32_t len = elements->length();
5129 if (range < len) len = range;
5130
5131 for (uint32_t j = 0; j < len; j++) {
5132 Handle<Object> e(elements->get(j));
5133 if (!e->IsTheHole()) {
5134 num_of_elements++;
5135 if (visitor)
5136 visitor->visit(j, e);
5137 }
5138 }
5139
5140 } else {
5141 Handle<Dictionary> dict(receiver->element_dictionary());
5142 uint32_t capacity = dict->Capacity();
5143 for (uint32_t j = 0; j < capacity; j++) {
5144 Handle<Object> k(dict->KeyAt(j));
5145 if (dict->IsKey(*k)) {
5146 ASSERT(k->IsNumber());
5147 uint32_t index = static_cast<uint32_t>(k->Number());
5148 if (index < range) {
5149 num_of_elements++;
5150 if (visitor) {
5151 visitor->visit(index,
5152 Handle<Object>(dict->ValueAt(j)));
5153 }
5154 }
5155 }
5156 }
5157 }
5158
5159 return num_of_elements;
5160}
5161
5162
5163/**
5164 * A helper function that visits elements of an Array object, and elements
5165 * on its prototypes.
5166 *
5167 * Elements on prototypes are visited first, and only elements whose indices
5168 * less than Array length are visited.
5169 *
5170 * If a ArrayConcatVisitor object is given, the visitor is called with
5171 * parameters, element's index + visitor_index_offset and the element.
5172 */
5173static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5174 ArrayConcatVisitor* visitor) {
5175 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5176 Handle<Object> obj = array;
5177
5178 static const int kEstimatedPrototypes = 3;
5179 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5180
5181 // Visit prototype first. If an element on the prototype is shadowed by
5182 // the inheritor using the same index, the ArrayConcatVisitor visits
5183 // the prototype element before the shadowing element.
5184 // The visitor can simply overwrite the old value by new value using
5185 // the same index. This follows Array::concat semantics.
5186 while (!obj->IsNull()) {
5187 objects.Add(Handle<JSObject>::cast(obj));
5188 obj = Handle<Object>(obj->GetPrototype());
5189 }
5190
5191 uint32_t nof_elements = 0;
5192 for (int i = objects.length() - 1; i >= 0; i--) {
5193 Handle<JSObject> obj = objects[i];
5194 nof_elements +=
5195 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5196 }
5197
5198 return nof_elements;
5199}
5200
5201
5202/**
5203 * A helper function of Runtime_ArrayConcat.
5204 *
5205 * The first argument is an Array of arrays and objects. It is the
5206 * same as the arguments array of Array::concat JS function.
5207 *
5208 * If an argument is an Array object, the function visits array
5209 * elements. If an argument is not an Array object, the function
5210 * visits the object as if it is an one-element array.
5211 *
5212 * If the result array index overflows 32-bit integer, the rounded
5213 * non-negative number is used as new length. For example, if one
5214 * array length is 2^32 - 1, second array length is 1, the
5215 * concatenated array length is 0.
5216 */
5217static uint32_t IterateArguments(Handle<JSArray> arguments,
5218 ArrayConcatVisitor* visitor) {
5219 uint32_t visited_elements = 0;
5220 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5221
5222 for (uint32_t i = 0; i < num_of_args; i++) {
5223 Handle<Object> obj(arguments->GetElement(i));
5224 if (obj->IsJSArray()) {
5225 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5226 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5227 uint32_t nof_elements =
5228 IterateArrayAndPrototypeElements(array, visitor);
5229 // Total elements of array and its prototype chain can be more than
5230 // the array length, but ArrayConcat can only concatenate at most
5231 // the array length number of elements.
5232 visited_elements += (nof_elements > len) ? len : nof_elements;
5233 if (visitor) visitor->increase_index_offset(len);
5234
5235 } else {
5236 if (visitor) {
5237 visitor->visit(0, obj);
5238 visitor->increase_index_offset(1);
5239 }
5240 visited_elements++;
5241 }
5242 }
5243 return visited_elements;
5244}
5245
5246
5247/**
5248 * Array::concat implementation.
5249 * See ECMAScript 262, 15.4.4.4.
5250 */
5251static Object* Runtime_ArrayConcat(Arguments args) {
5252 ASSERT(args.length() == 1);
5253 HandleScope handle_scope;
5254
5255 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5256 Handle<JSArray> arguments(arg_arrays);
5257
5258 // Pass 1: estimate the number of elements of the result
5259 // (it could be more than real numbers if prototype has elements).
5260 uint32_t result_length = 0;
5261 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5262
5263 { AssertNoAllocation nogc;
5264 for (uint32_t i = 0; i < num_of_args; i++) {
5265 Object* obj = arguments->GetElement(i);
5266 if (obj->IsJSArray()) {
5267 result_length +=
5268 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5269 } else {
5270 result_length++;
5271 }
5272 }
5273 }
5274
5275 // Allocate an empty array, will set length and content later.
5276 Handle<JSArray> result = Factory::NewJSArray(0);
5277
5278 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5279 // If estimated number of elements is more than half of length, a
5280 // fixed array (fast case) is more time and space-efficient than a
5281 // dictionary.
5282 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5283
5284 Handle<FixedArray> storage;
5285 if (fast_case) {
5286 // The backing storage array must have non-existing elements to
5287 // preserve holes across concat operations.
5288 storage = Factory::NewFixedArrayWithHoles(result_length);
5289
5290 } else {
5291 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5292 uint32_t at_least_space_for = estimate_nof_elements +
5293 (estimate_nof_elements >> 2);
5294 storage = Handle<FixedArray>::cast(
5295 Factory::NewDictionary(at_least_space_for));
5296 }
5297
5298 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5299
5300 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5301
5302 IterateArguments(arguments, &visitor);
5303
5304 result->set_length(*len);
5305 result->set_elements(*storage);
5306
5307 return *result;
5308}
5309
5310
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005311// This will not allocate (flatten the string), but it may run
5312// very slowly for very deeply nested ConsStrings. For debugging use only.
5313static Object* Runtime_GlobalPrint(Arguments args) {
5314 NoHandleAllocation ha;
5315 ASSERT(args.length() == 1);
5316
5317 CONVERT_CHECKED(String, string, args[0]);
5318 StringInputBuffer buffer(string);
5319 while (buffer.has_more()) {
5320 uint16_t character = buffer.GetNext();
5321 PrintF("%c", character);
5322 }
5323 return string;
5324}
5325
ager@chromium.org5ec48922009-05-05 07:25:34 +00005326// Moves all own elements of an object, that are below a limit, to positions
5327// starting at zero. All undefined values are placed after non-undefined values,
5328// and are followed by non-existing element. Does not change the length
5329// property.
5330// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005331static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005332 ASSERT(args.length() == 2);
5333 CONVERT_CHECKED(JSObject, object, args[0]);
5334 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5335 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005336}
5337
5338
5339// Move contents of argument 0 (an array) to argument 1 (an array)
5340static Object* Runtime_MoveArrayContents(Arguments args) {
5341 ASSERT(args.length() == 2);
5342 CONVERT_CHECKED(JSArray, from, args[0]);
5343 CONVERT_CHECKED(JSArray, to, args[1]);
5344 to->SetContent(FixedArray::cast(from->elements()));
5345 to->set_length(from->length());
5346 from->SetContent(Heap::empty_fixed_array());
5347 from->set_length(0);
5348 return to;
5349}
5350
5351
5352// How many elements does this array have?
5353static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5354 ASSERT(args.length() == 1);
5355 CONVERT_CHECKED(JSArray, array, args[0]);
5356 HeapObject* elements = array->elements();
5357 if (elements->IsDictionary()) {
5358 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5359 } else {
5360 return array->length();
5361 }
5362}
5363
5364
5365// Returns an array that tells you where in the [0, length) interval an array
5366// might have elements. Can either return keys or intervals. Keys can have
5367// gaps in (undefined). Intervals can also span over some undefined keys.
5368static Object* Runtime_GetArrayKeys(Arguments args) {
5369 ASSERT(args.length() == 2);
5370 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005371 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005372 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005373 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005374 // Create an array and get all the keys into it, then remove all the
5375 // keys that are not integers in the range 0 to length-1.
5376 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5377 int keys_length = keys->length();
5378 for (int i = 0; i < keys_length; i++) {
5379 Object* key = keys->get(i);
5380 uint32_t index;
5381 if (!Array::IndexFromObject(key, &index) || index >= length) {
5382 // Zap invalid keys.
5383 keys->set_undefined(i);
5384 }
5385 }
5386 return *Factory::NewJSArrayWithElements(keys);
5387 } else {
5388 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5389 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005390 single_interval->set(0,
5391 Smi::FromInt(-1),
5392 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005393 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5394 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005395 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005396 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005397 single_interval->set(1, *length_object);
5398 return *Factory::NewJSArrayWithElements(single_interval);
5399 }
5400}
5401
5402
5403// DefineAccessor takes an optional final argument which is the
5404// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5405// to the way accessors are implemented, it is set for both the getter
5406// and setter on the first call to DefineAccessor and ignored on
5407// subsequent calls.
5408static Object* Runtime_DefineAccessor(Arguments args) {
5409 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5410 // Compute attributes.
5411 PropertyAttributes attributes = NONE;
5412 if (args.length() == 5) {
5413 CONVERT_CHECKED(Smi, attrs, args[4]);
5414 int value = attrs->value();
5415 // Only attribute bits should be set.
5416 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5417 attributes = static_cast<PropertyAttributes>(value);
5418 }
5419
5420 CONVERT_CHECKED(JSObject, obj, args[0]);
5421 CONVERT_CHECKED(String, name, args[1]);
5422 CONVERT_CHECKED(Smi, flag, args[2]);
5423 CONVERT_CHECKED(JSFunction, fun, args[3]);
5424 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5425}
5426
5427
5428static Object* Runtime_LookupAccessor(Arguments args) {
5429 ASSERT(args.length() == 3);
5430 CONVERT_CHECKED(JSObject, obj, args[0]);
5431 CONVERT_CHECKED(String, name, args[1]);
5432 CONVERT_CHECKED(Smi, flag, args[2]);
5433 return obj->LookupAccessor(name, flag->value() == 0);
5434}
5435
5436
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005437#ifdef ENABLE_DEBUGGER_SUPPORT
5438static Object* Runtime_DebugBreak(Arguments args) {
5439 ASSERT(args.length() == 0);
5440 return Execution::DebugBreakHelper();
5441}
5442
5443
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005444// Helper functions for wrapping and unwrapping stack frame ids.
5445static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005446 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005447 return Smi::FromInt(id >> 2);
5448}
5449
5450
5451static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5452 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5453}
5454
5455
5456// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005457// args[0]: debug event listener function to set or null or undefined for
5458// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005459// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005460static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005461 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005462 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5463 args[0]->IsUndefined() ||
5464 args[0]->IsNull());
5465 Handle<Object> callback = args.at<Object>(0);
5466 Handle<Object> data = args.at<Object>(1);
5467 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005468
5469 return Heap::undefined_value();
5470}
5471
5472
5473static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005474 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005475 StackGuard::DebugBreak();
5476 return Heap::undefined_value();
5477}
5478
5479
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005480// Find the length of the prototype chain that is to to handled as one. If a
5481// prototype object is hidden it is to be viewed as part of the the object it
5482// is prototype for.
5483static int LocalPrototypeChainLength(JSObject* obj) {
5484 int count = 1;
5485 Object* proto = obj->GetPrototype();
5486 while (proto->IsJSObject() &&
5487 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5488 count++;
5489 proto = JSObject::cast(proto)->GetPrototype();
5490 }
5491 return count;
5492}
5493
5494
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005495static Object* DebugLookupResultValue(Object* receiver, String* name,
5496 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005497 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005498 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005499 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005500 case NORMAL: {
5501 Dictionary* dict =
5502 JSObject::cast(result->holder())->property_dictionary();
5503 value = dict->ValueAt(result->GetDictionaryEntry());
5504 if (value->IsTheHole()) {
5505 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005506 }
5507 return value;
5508 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005509 case FIELD:
5510 value =
5511 JSObject::cast(
5512 result->holder())->FastPropertyAt(result->GetFieldIndex());
5513 if (value->IsTheHole()) {
5514 return Heap::undefined_value();
5515 }
5516 return value;
5517 case CONSTANT_FUNCTION:
5518 return result->GetConstantFunction();
5519 case CALLBACKS: {
5520 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005521 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005522 value = receiver->GetPropertyWithCallback(
5523 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005524 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005525 value = Top::pending_exception();
5526 Top::clear_pending_exception();
5527 if (caught_exception != NULL) {
5528 *caught_exception = true;
5529 }
5530 }
5531 return value;
5532 } else {
5533 return Heap::undefined_value();
5534 }
5535 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005536 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005537 case MAP_TRANSITION:
5538 case CONSTANT_TRANSITION:
5539 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 return Heap::undefined_value();
5541 default:
5542 UNREACHABLE();
5543 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005544 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005545 return Heap::undefined_value();
5546}
5547
5548
ager@chromium.org32912102009-01-16 10:38:43 +00005549// Get debugger related details for an object property.
5550// args[0]: object holding property
5551// args[1]: name of the property
5552//
5553// The array returned contains the following information:
5554// 0: Property value
5555// 1: Property details
5556// 2: Property value is exception
5557// 3: Getter function if defined
5558// 4: Setter function if defined
5559// Items 2-4 are only filled if the property has either a getter or a setter
5560// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005561static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005562 HandleScope scope;
5563
5564 ASSERT(args.length() == 2);
5565
5566 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5567 CONVERT_ARG_CHECKED(String, name, 1);
5568
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005569 // Make sure to set the current context to the context before the debugger was
5570 // entered (if the debugger is entered). The reason for switching context here
5571 // is that for some property lookups (accessors and interceptors) callbacks
5572 // into the embedding application can occour, and the embedding application
5573 // could have the assumption that its own global context is the current
5574 // context and not some internal debugger context.
5575 SaveContext save;
5576 if (Debug::InDebugger()) {
5577 Top::set_context(*Debug::debugger_entry()->GetContext());
5578 }
5579
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005580 // Skip the global proxy as it has no properties and always delegates to the
5581 // real global object.
5582 if (obj->IsJSGlobalProxy()) {
5583 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5584 }
5585
5586
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005587 // Check if the name is trivially convertible to an index and get the element
5588 // if so.
5589 uint32_t index;
5590 if (name->AsArrayIndex(&index)) {
5591 Handle<FixedArray> details = Factory::NewFixedArray(2);
5592 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5593 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5594 return *Factory::NewJSArrayWithElements(details);
5595 }
5596
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005597 // Find the number of objects making up this.
5598 int length = LocalPrototypeChainLength(*obj);
5599
5600 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005601 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005602 Handle<JSObject> jsproto = obj;
5603 for (int i = 0; i < length; i++) {
5604 jsproto->LocalLookup(*name, &result);
5605 if (result.IsProperty()) {
5606 break;
5607 }
5608 if (i < length - 1) {
5609 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5610 }
5611 }
5612
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005613 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005614 // LookupResult is not GC safe as all its members are raw object pointers.
5615 // When calling DebugLookupResultValue GC can happen as this might invoke
5616 // callbacks. After the call to DebugLookupResultValue the callback object
5617 // in the LookupResult might still be needed. Put it into a handle for later
5618 // use.
5619 PropertyType result_type = result.type();
5620 Handle<Object> result_callback_obj;
5621 if (result_type == CALLBACKS) {
5622 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5623 }
5624
5625 // Find the actual value. Don't use result after this call as it's content
5626 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005627 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005628 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005629 &caught_exception);
5630 if (value->IsFailure()) return value;
5631 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005632
ager@chromium.org32912102009-01-16 10:38:43 +00005633 // If the callback object is a fixed array then it contains JavaScript
5634 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005635 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5636 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005637 Handle<FixedArray> details =
5638 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005639 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005640 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005641 if (hasJavaScriptAccessors) {
5642 details->set(2,
5643 caught_exception ? Heap::true_value() : Heap::false_value());
5644 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5645 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5646 }
5647
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005648 return *Factory::NewJSArrayWithElements(details);
5649 }
5650 return Heap::undefined_value();
5651}
5652
5653
5654static Object* Runtime_DebugGetProperty(Arguments args) {
5655 HandleScope scope;
5656
5657 ASSERT(args.length() == 2);
5658
5659 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5660 CONVERT_ARG_CHECKED(String, name, 1);
5661
5662 LookupResult result;
5663 obj->Lookup(*name, &result);
5664 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005665 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005666 }
5667 return Heap::undefined_value();
5668}
5669
5670
5671// Return the names of the local named properties.
5672// args[0]: object
5673static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5674 HandleScope scope;
5675 ASSERT(args.length() == 1);
5676 if (!args[0]->IsJSObject()) {
5677 return Heap::undefined_value();
5678 }
5679 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5680
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005681 // Skip the global proxy as it has no properties and always delegates to the
5682 // real global object.
5683 if (obj->IsJSGlobalProxy()) {
5684 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5685 }
5686
5687 // Find the number of objects making up this.
5688 int length = LocalPrototypeChainLength(*obj);
5689
5690 // Find the number of local properties for each of the objects.
5691 int* local_property_count = NewArray<int>(length);
5692 int total_property_count = 0;
5693 Handle<JSObject> jsproto = obj;
5694 for (int i = 0; i < length; i++) {
5695 int n;
5696 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5697 local_property_count[i] = n;
5698 total_property_count += n;
5699 if (i < length - 1) {
5700 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5701 }
5702 }
5703
5704 // Allocate an array with storage for all the property names.
5705 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5706
5707 // Get the property names.
5708 jsproto = obj;
5709 for (int i = 0; i < length; i++) {
5710 jsproto->GetLocalPropertyNames(*names,
5711 i == 0 ? 0 : local_property_count[i - 1]);
5712 if (i < length - 1) {
5713 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5714 }
5715 }
5716
5717 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005718 return *Factory::NewJSArrayWithElements(names);
5719}
5720
5721
5722// Return the names of the local indexed properties.
5723// args[0]: object
5724static Object* Runtime_DebugLocalElementNames(Arguments args) {
5725 HandleScope scope;
5726 ASSERT(args.length() == 1);
5727 if (!args[0]->IsJSObject()) {
5728 return Heap::undefined_value();
5729 }
5730 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5731
5732 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5733 Handle<FixedArray> names = Factory::NewFixedArray(n);
5734 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5735 return *Factory::NewJSArrayWithElements(names);
5736}
5737
5738
5739// Return the property type calculated from the property details.
5740// args[0]: smi with property details.
5741static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5742 ASSERT(args.length() == 1);
5743 CONVERT_CHECKED(Smi, details, args[0]);
5744 PropertyType type = PropertyDetails(details).type();
5745 return Smi::FromInt(static_cast<int>(type));
5746}
5747
5748
5749// Return the property attribute calculated from the property details.
5750// args[0]: smi with property details.
5751static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5752 ASSERT(args.length() == 1);
5753 CONVERT_CHECKED(Smi, details, args[0]);
5754 PropertyAttributes attributes = PropertyDetails(details).attributes();
5755 return Smi::FromInt(static_cast<int>(attributes));
5756}
5757
5758
5759// Return the property insertion index calculated from the property details.
5760// args[0]: smi with property details.
5761static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5762 ASSERT(args.length() == 1);
5763 CONVERT_CHECKED(Smi, details, args[0]);
5764 int index = PropertyDetails(details).index();
5765 return Smi::FromInt(index);
5766}
5767
5768
5769// Return information on whether an object has a named or indexed interceptor.
5770// args[0]: object
5771static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5772 HandleScope scope;
5773 ASSERT(args.length() == 1);
5774 if (!args[0]->IsJSObject()) {
5775 return Smi::FromInt(0);
5776 }
5777 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5778
5779 int result = 0;
5780 if (obj->HasNamedInterceptor()) result |= 2;
5781 if (obj->HasIndexedInterceptor()) result |= 1;
5782
5783 return Smi::FromInt(result);
5784}
5785
5786
5787// Return property names from named interceptor.
5788// args[0]: object
5789static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5790 HandleScope scope;
5791 ASSERT(args.length() == 1);
5792 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793
ager@chromium.org32912102009-01-16 10:38:43 +00005794 if (obj->HasNamedInterceptor()) {
5795 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5796 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5797 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798 return Heap::undefined_value();
5799}
5800
5801
5802// Return element names from indexed interceptor.
5803// args[0]: object
5804static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5805 HandleScope scope;
5806 ASSERT(args.length() == 1);
5807 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808
ager@chromium.org32912102009-01-16 10:38:43 +00005809 if (obj->HasIndexedInterceptor()) {
5810 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5811 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5812 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005813 return Heap::undefined_value();
5814}
5815
5816
5817// Return property value from named interceptor.
5818// args[0]: object
5819// args[1]: property name
5820static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5821 HandleScope scope;
5822 ASSERT(args.length() == 2);
5823 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5824 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5825 CONVERT_ARG_CHECKED(String, name, 1);
5826
5827 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005828 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005829}
5830
5831
5832// Return element value from indexed interceptor.
5833// args[0]: object
5834// args[1]: index
5835static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5836 HandleScope scope;
5837 ASSERT(args.length() == 2);
5838 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5839 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5840 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5841
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005842 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005843}
5844
5845
5846static Object* Runtime_CheckExecutionState(Arguments args) {
5847 ASSERT(args.length() >= 1);
5848 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005849 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005850 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005851 return Top::Throw(Heap::illegal_execution_state_symbol());
5852 }
5853
5854 return Heap::true_value();
5855}
5856
5857
5858static Object* Runtime_GetFrameCount(Arguments args) {
5859 HandleScope scope;
5860 ASSERT(args.length() == 1);
5861
5862 // Check arguments.
5863 Object* result = Runtime_CheckExecutionState(args);
5864 if (result->IsFailure()) return result;
5865
5866 // Count all frames which are relevant to debugging stack trace.
5867 int n = 0;
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 is no JavaScript stack frame count is 0.
5871 return Smi::FromInt(0);
5872 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005873 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5874 return Smi::FromInt(n);
5875}
5876
5877
5878static const int kFrameDetailsFrameIdIndex = 0;
5879static const int kFrameDetailsReceiverIndex = 1;
5880static const int kFrameDetailsFunctionIndex = 2;
5881static const int kFrameDetailsArgumentCountIndex = 3;
5882static const int kFrameDetailsLocalCountIndex = 4;
5883static const int kFrameDetailsSourcePositionIndex = 5;
5884static const int kFrameDetailsConstructCallIndex = 6;
5885static const int kFrameDetailsDebuggerFrameIndex = 7;
5886static const int kFrameDetailsFirstDynamicIndex = 8;
5887
5888// Return an array with frame details
5889// args[0]: number: break id
5890// args[1]: number: frame index
5891//
5892// The array returned contains the following information:
5893// 0: Frame id
5894// 1: Receiver
5895// 2: Function
5896// 3: Argument count
5897// 4: Local count
5898// 5: Source position
5899// 6: Constructor call
5900// 7: Debugger frame
5901// Arguments name, value
5902// Locals name, value
5903static Object* Runtime_GetFrameDetails(Arguments args) {
5904 HandleScope scope;
5905 ASSERT(args.length() == 2);
5906
5907 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005908 Object* check = Runtime_CheckExecutionState(args);
5909 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005910 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5911
5912 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005913 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005914 if (id == StackFrame::NO_ID) {
5915 // If there are no JavaScript stack frames return undefined.
5916 return Heap::undefined_value();
5917 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005918 int count = 0;
5919 JavaScriptFrameIterator it(id);
5920 for (; !it.done(); it.Advance()) {
5921 if (count == index) break;
5922 count++;
5923 }
5924 if (it.done()) return Heap::undefined_value();
5925
5926 // Traverse the saved contexts chain to find the active context for the
5927 // selected frame.
5928 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005929 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005930 save = save->prev();
5931 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005932 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005933
5934 // Get the frame id.
5935 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5936
5937 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005938 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005939
5940 // Check for constructor frame.
5941 bool constructor = it.frame()->IsConstructor();
5942
5943 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005944 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005945 ScopeInfo<> info(*code);
5946
5947 // Get the context.
5948 Handle<Context> context(Context::cast(it.frame()->context()));
5949
5950 // Get the locals names and values into a temporary array.
5951 //
5952 // TODO(1240907): Hide compiler-introduced stack variables
5953 // (e.g. .result)? For users of the debugger, they will probably be
5954 // confusing.
5955 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5956 for (int i = 0; i < info.NumberOfLocals(); i++) {
5957 // Name of the local.
5958 locals->set(i * 2, *info.LocalName(i));
5959
5960 // Fetch the value of the local - either from the stack or from a
5961 // heap-allocated context.
5962 if (i < info.number_of_stack_slots()) {
5963 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
5964 } else {
5965 Handle<String> name = info.LocalName(i);
5966 // Traverse the context chain to the function context as all local
5967 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005968 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005969 context = Handle<Context>(context->previous());
5970 }
5971 ASSERT(context->is_function_context());
5972 locals->set(i * 2 + 1,
5973 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
5974 NULL)));
5975 }
5976 }
5977
5978 // Now advance to the arguments adapter frame (if any). If contains all
5979 // the provided parameters and
5980
5981 // Now advance to the arguments adapter frame (if any). It contains all
5982 // the provided parameters whereas the function frame always have the number
5983 // of arguments matching the functions parameters. The rest of the
5984 // information (except for what is collected above) is the same.
5985 it.AdvanceToArgumentsFrame();
5986
5987 // Find the number of arguments to fill. At least fill the number of
5988 // parameters for the function and fill more if more parameters are provided.
5989 int argument_count = info.number_of_parameters();
5990 if (argument_count < it.frame()->GetProvidedParametersCount()) {
5991 argument_count = it.frame()->GetProvidedParametersCount();
5992 }
5993
5994 // Calculate the size of the result.
5995 int details_size = kFrameDetailsFirstDynamicIndex +
5996 2 * (argument_count + info.NumberOfLocals());
5997 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
5998
5999 // Add the frame id.
6000 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6001
6002 // Add the function (same as in function frame).
6003 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6004
6005 // Add the arguments count.
6006 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6007
6008 // Add the locals count
6009 details->set(kFrameDetailsLocalCountIndex,
6010 Smi::FromInt(info.NumberOfLocals()));
6011
6012 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006013 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006014 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6015 } else {
6016 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6017 }
6018
6019 // Add the constructor information.
6020 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6021
6022 // Add information on whether this frame is invoked in the debugger context.
6023 details->set(kFrameDetailsDebuggerFrameIndex,
6024 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6025
6026 // Fill the dynamic part.
6027 int details_index = kFrameDetailsFirstDynamicIndex;
6028
6029 // Add arguments name and value.
6030 for (int i = 0; i < argument_count; i++) {
6031 // Name of the argument.
6032 if (i < info.number_of_parameters()) {
6033 details->set(details_index++, *info.parameter_name(i));
6034 } else {
6035 details->set(details_index++, Heap::undefined_value());
6036 }
6037
6038 // Parameter value.
6039 if (i < it.frame()->GetProvidedParametersCount()) {
6040 details->set(details_index++, it.frame()->GetParameter(i));
6041 } else {
6042 details->set(details_index++, Heap::undefined_value());
6043 }
6044 }
6045
6046 // Add locals name and value from the temporary copy from the function frame.
6047 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6048 details->set(details_index++, locals->get(i));
6049 }
6050
6051 // Add the receiver (same as in function frame).
6052 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6053 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6054 Handle<Object> receiver(it.frame()->receiver());
6055 if (!receiver->IsJSObject()) {
6056 // If the receiver is NOT a JSObject we have hit an optimization
6057 // where a value object is not converted into a wrapped JS objects.
6058 // To hide this optimization from the debugger, we wrap the receiver
6059 // by creating correct wrapper object based on the calling frame's
6060 // global context.
6061 it.Advance();
6062 Handle<Context> calling_frames_global_context(
6063 Context::cast(Context::cast(it.frame()->context())->global_context()));
6064 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6065 }
6066 details->set(kFrameDetailsReceiverIndex, *receiver);
6067
6068 ASSERT_EQ(details_size, details_index);
6069 return *Factory::NewJSArrayWithElements(details);
6070}
6071
6072
6073static Object* Runtime_GetCFrames(Arguments args) {
6074 HandleScope scope;
6075 ASSERT(args.length() == 1);
6076 Object* result = Runtime_CheckExecutionState(args);
6077 if (result->IsFailure()) return result;
6078
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006079#if V8_HOST_ARCH_64_BIT
6080 UNIMPLEMENTED();
6081 return Heap::undefined_value();
6082#else
6083
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006084 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006085 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6086 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087 if (frames_count == OS::kStackWalkError) {
6088 return Heap::undefined_value();
6089 }
6090
6091 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6092 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6093 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6094 for (int i = 0; i < frames_count; i++) {
6095 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6096 frame_value->SetProperty(
6097 *address_str,
6098 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6099 NONE);
6100
6101 // Get the stack walk text for this frame.
6102 Handle<String> frame_text;
6103 if (strlen(frames[i].text) > 0) {
6104 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6105 frame_text = Factory::NewStringFromAscii(str);
6106 }
6107
6108 if (!frame_text.is_null()) {
6109 frame_value->SetProperty(*text_str, *frame_text, NONE);
6110 }
6111
6112 frames_array->set(i, *frame_value);
6113 }
6114 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006115#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006116}
6117
6118
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006119static Object* Runtime_GetThreadCount(Arguments args) {
6120 HandleScope scope;
6121 ASSERT(args.length() == 1);
6122
6123 // Check arguments.
6124 Object* result = Runtime_CheckExecutionState(args);
6125 if (result->IsFailure()) return result;
6126
6127 // Count all archived V8 threads.
6128 int n = 0;
6129 for (ThreadState* thread = ThreadState::FirstInUse();
6130 thread != NULL;
6131 thread = thread->Next()) {
6132 n++;
6133 }
6134
6135 // Total number of threads is current thread and archived threads.
6136 return Smi::FromInt(n + 1);
6137}
6138
6139
6140static const int kThreadDetailsCurrentThreadIndex = 0;
6141static const int kThreadDetailsThreadIdIndex = 1;
6142static const int kThreadDetailsSize = 2;
6143
6144// Return an array with thread details
6145// args[0]: number: break id
6146// args[1]: number: thread index
6147//
6148// The array returned contains the following information:
6149// 0: Is current thread?
6150// 1: Thread id
6151static Object* Runtime_GetThreadDetails(Arguments args) {
6152 HandleScope scope;
6153 ASSERT(args.length() == 2);
6154
6155 // Check arguments.
6156 Object* check = Runtime_CheckExecutionState(args);
6157 if (check->IsFailure()) return check;
6158 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6159
6160 // Allocate array for result.
6161 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6162
6163 // Thread index 0 is current thread.
6164 if (index == 0) {
6165 // Fill the details.
6166 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6167 details->set(kThreadDetailsThreadIdIndex,
6168 Smi::FromInt(ThreadManager::CurrentId()));
6169 } else {
6170 // Find the thread with the requested index.
6171 int n = 1;
6172 ThreadState* thread = ThreadState::FirstInUse();
6173 while (index != n && thread != NULL) {
6174 thread = thread->Next();
6175 n++;
6176 }
6177 if (thread == NULL) {
6178 return Heap::undefined_value();
6179 }
6180
6181 // Fill the details.
6182 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6183 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6184 }
6185
6186 // Convert to JS array and return.
6187 return *Factory::NewJSArrayWithElements(details);
6188}
6189
6190
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006191static Object* Runtime_GetBreakLocations(Arguments args) {
6192 HandleScope scope;
6193 ASSERT(args.length() == 1);
6194
6195 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6196 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6197 // Find the number of break points
6198 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6199 if (break_locations->IsUndefined()) return Heap::undefined_value();
6200 // Return array as JS array
6201 return *Factory::NewJSArrayWithElements(
6202 Handle<FixedArray>::cast(break_locations));
6203}
6204
6205
6206// Set a break point in a function
6207// args[0]: function
6208// args[1]: number: break source position (within the function source)
6209// args[2]: number: break point object
6210static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6211 HandleScope scope;
6212 ASSERT(args.length() == 3);
6213 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6214 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6215 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6216 RUNTIME_ASSERT(source_position >= 0);
6217 Handle<Object> break_point_object_arg = args.at<Object>(2);
6218
6219 // Set break point.
6220 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6221
6222 return Heap::undefined_value();
6223}
6224
6225
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006226Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6227 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006228 // Iterate the heap looking for SharedFunctionInfo generated from the
6229 // script. The inner most SharedFunctionInfo containing the source position
6230 // for the requested break point is found.
6231 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6232 // which is found is not compiled it is compiled and the heap is iterated
6233 // again as the compilation might create inner functions from the newly
6234 // compiled function and the actual requested break point might be in one of
6235 // these functions.
6236 bool done = false;
6237 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006238 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006239 Handle<SharedFunctionInfo> target;
6240 // The current candidate for the last function in script:
6241 Handle<SharedFunctionInfo> last;
6242 while (!done) {
6243 HeapIterator iterator;
6244 while (iterator.has_next()) {
6245 HeapObject* obj = iterator.next();
6246 ASSERT(obj != NULL);
6247 if (obj->IsSharedFunctionInfo()) {
6248 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6249 if (shared->script() == *script) {
6250 // If the SharedFunctionInfo found has the requested script data and
6251 // contains the source position it is a candidate.
6252 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006253 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006254 start_position = shared->start_position();
6255 }
6256 if (start_position <= position &&
6257 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006258 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006259 // candidate this is the new candidate.
6260 if (target.is_null()) {
6261 target_start_position = start_position;
6262 target = shared;
6263 } else {
6264 if (target_start_position < start_position &&
6265 shared->end_position() < target->end_position()) {
6266 target_start_position = start_position;
6267 target = shared;
6268 }
6269 }
6270 }
6271
6272 // Keep track of the last function in the script.
6273 if (last.is_null() ||
6274 shared->end_position() > last->start_position()) {
6275 last = shared;
6276 }
6277 }
6278 }
6279 }
6280
6281 // Make sure some candidate is selected.
6282 if (target.is_null()) {
6283 if (!last.is_null()) {
6284 // Position after the last function - use last.
6285 target = last;
6286 } else {
6287 // Unable to find function - possibly script without any function.
6288 return Heap::undefined_value();
6289 }
6290 }
6291
6292 // If the candidate found is compiled we are done. NOTE: when lazy
6293 // compilation of inner functions is introduced some additional checking
6294 // needs to be done here to compile inner functions.
6295 done = target->is_compiled();
6296 if (!done) {
6297 // If the candidate is not compiled compile it to reveal any inner
6298 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006299 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006300 }
6301 }
6302
6303 return *target;
6304}
6305
6306
6307// Change the state of a break point in a script. NOTE: Regarding performance
6308// see the NOTE for GetScriptFromScriptData.
6309// args[0]: script to set break point in
6310// args[1]: number: break source position (within the script source)
6311// args[2]: number: break point object
6312static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6313 HandleScope scope;
6314 ASSERT(args.length() == 3);
6315 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6316 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6317 RUNTIME_ASSERT(source_position >= 0);
6318 Handle<Object> break_point_object_arg = args.at<Object>(2);
6319
6320 // Get the script from the script wrapper.
6321 RUNTIME_ASSERT(wrapper->value()->IsScript());
6322 Handle<Script> script(Script::cast(wrapper->value()));
6323
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006324 Object* result = Runtime::FindSharedFunctionInfoInScript(
6325 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006326 if (!result->IsUndefined()) {
6327 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6328 // Find position within function. The script position might be before the
6329 // source position of the first function.
6330 int position;
6331 if (shared->start_position() > source_position) {
6332 position = 0;
6333 } else {
6334 position = source_position - shared->start_position();
6335 }
6336 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6337 }
6338 return Heap::undefined_value();
6339}
6340
6341
6342// Clear a break point
6343// args[0]: number: break point object
6344static Object* Runtime_ClearBreakPoint(Arguments args) {
6345 HandleScope scope;
6346 ASSERT(args.length() == 1);
6347 Handle<Object> break_point_object_arg = args.at<Object>(0);
6348
6349 // Clear break point.
6350 Debug::ClearBreakPoint(break_point_object_arg);
6351
6352 return Heap::undefined_value();
6353}
6354
6355
6356// Change the state of break on exceptions
6357// args[0]: boolean indicating uncaught exceptions
6358// args[1]: boolean indicating on/off
6359static Object* Runtime_ChangeBreakOnException(Arguments args) {
6360 HandleScope scope;
6361 ASSERT(args.length() == 2);
6362 ASSERT(args[0]->IsNumber());
6363 ASSERT(args[1]->IsBoolean());
6364
6365 // Update break point state
6366 ExceptionBreakType type =
6367 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6368 bool enable = args[1]->ToBoolean()->IsTrue();
6369 Debug::ChangeBreakOnException(type, enable);
6370 return Heap::undefined_value();
6371}
6372
6373
6374// Prepare for stepping
6375// args[0]: break id for checking execution state
6376// args[1]: step action from the enumeration StepAction
6377// args[2]: number of times to perform the step
6378static Object* Runtime_PrepareStep(Arguments args) {
6379 HandleScope scope;
6380 ASSERT(args.length() == 3);
6381 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006382 Object* check = Runtime_CheckExecutionState(args);
6383 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006384 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6385 return Top::Throw(Heap::illegal_argument_symbol());
6386 }
6387
6388 // Get the step action and check validity.
6389 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6390 if (step_action != StepIn &&
6391 step_action != StepNext &&
6392 step_action != StepOut &&
6393 step_action != StepInMin &&
6394 step_action != StepMin) {
6395 return Top::Throw(Heap::illegal_argument_symbol());
6396 }
6397
6398 // Get the number of steps.
6399 int step_count = NumberToInt32(args[2]);
6400 if (step_count < 1) {
6401 return Top::Throw(Heap::illegal_argument_symbol());
6402 }
6403
6404 // Prepare step.
6405 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6406 return Heap::undefined_value();
6407}
6408
6409
6410// Clear all stepping set by PrepareStep.
6411static Object* Runtime_ClearStepping(Arguments args) {
6412 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006413 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006414 Debug::ClearStepping();
6415 return Heap::undefined_value();
6416}
6417
6418
6419// Creates a copy of the with context chain. The copy of the context chain is
6420// is linked to the function context supplied.
6421static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6422 Handle<Context> function_context) {
6423 // At the bottom of the chain. Return the function context to link to.
6424 if (context_chain->is_function_context()) {
6425 return function_context;
6426 }
6427
6428 // Recursively copy the with contexts.
6429 Handle<Context> previous(context_chain->previous());
6430 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6431 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006432 CopyWithContextChain(function_context, previous),
6433 extension,
6434 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006435}
6436
6437
6438// Helper function to find or create the arguments object for
6439// Runtime_DebugEvaluate.
6440static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6441 Handle<JSFunction> function,
6442 Handle<Code> code,
6443 const ScopeInfo<>* sinfo,
6444 Handle<Context> function_context) {
6445 // Try to find the value of 'arguments' to pass as parameter. If it is not
6446 // found (that is the debugged function does not reference 'arguments' and
6447 // does not support eval) then create an 'arguments' object.
6448 int index;
6449 if (sinfo->number_of_stack_slots() > 0) {
6450 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6451 if (index != -1) {
6452 return Handle<Object>(frame->GetExpression(index));
6453 }
6454 }
6455
6456 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6457 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6458 NULL);
6459 if (index != -1) {
6460 return Handle<Object>(function_context->get(index));
6461 }
6462 }
6463
6464 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006465 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6466 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006467 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006468 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006469 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006470 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006471 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006472 return arguments;
6473}
6474
6475
6476// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006477// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006478// extension part has all the parameters and locals of the function on the
6479// stack frame. A function which calls eval with the code to evaluate is then
6480// compiled in this context and called in this context. As this context
6481// replaces the context of the function on the stack frame a new (empty)
6482// function is created as well to be used as the closure for the context.
6483// This function and the context acts as replacements for the function on the
6484// stack frame presenting the same view of the values of parameters and
6485// local variables as if the piece of JavaScript was evaluated at the point
6486// where the function on the stack frame is currently stopped.
6487static Object* Runtime_DebugEvaluate(Arguments args) {
6488 HandleScope scope;
6489
6490 // Check the execution state and decode arguments frame and source to be
6491 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006492 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006493 Object* check_result = Runtime_CheckExecutionState(args);
6494 if (check_result->IsFailure()) return check_result;
6495 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6496 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006497 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6498
6499 // Handle the processing of break.
6500 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006501
6502 // Get the frame where the debugging is performed.
6503 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6504 JavaScriptFrameIterator it(id);
6505 JavaScriptFrame* frame = it.frame();
6506 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6507 Handle<Code> code(function->code());
6508 ScopeInfo<> sinfo(*code);
6509
6510 // Traverse the saved contexts chain to find the active context for the
6511 // selected frame.
6512 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006513 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006514 save = save->prev();
6515 }
6516 ASSERT(save != NULL);
6517 SaveContext savex;
6518 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006519
6520 // Create the (empty) function replacing the function on the stack frame for
6521 // the purpose of evaluating in the context created below. It is important
6522 // that this function does not describe any parameters and local variables
6523 // in the context. If it does then this will cause problems with the lookup
6524 // in Context::Lookup, where context slots for parameters and local variables
6525 // are looked at before the extension object.
6526 Handle<JSFunction> go_between =
6527 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6528 go_between->set_context(function->context());
6529#ifdef DEBUG
6530 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6531 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6532 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6533#endif
6534
6535 // Allocate and initialize a context extension object with all the
6536 // arguments, stack locals heap locals and extension properties of the
6537 // debugged function.
6538 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6539 // First fill all parameters to the context extension.
6540 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6541 SetProperty(context_ext,
6542 sinfo.parameter_name(i),
6543 Handle<Object>(frame->GetParameter(i)), NONE);
6544 }
6545 // Second fill all stack locals to the context extension.
6546 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6547 SetProperty(context_ext,
6548 sinfo.stack_slot_name(i),
6549 Handle<Object>(frame->GetExpression(i)), NONE);
6550 }
6551 // Third fill all context locals to the context extension.
6552 Handle<Context> frame_context(Context::cast(frame->context()));
6553 Handle<Context> function_context(frame_context->fcontext());
6554 for (int i = Context::MIN_CONTEXT_SLOTS;
6555 i < sinfo.number_of_context_slots();
6556 ++i) {
6557 int context_index =
6558 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6559 SetProperty(context_ext,
6560 sinfo.context_slot_name(i),
6561 Handle<Object>(function_context->get(context_index)), NONE);
6562 }
6563 // Finally copy any properties from the function context extension. This will
6564 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006565 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006566 !function_context->IsGlobalContext()) {
6567 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6568 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6569 for (int i = 0; i < keys->length(); i++) {
6570 // Names of variables introduced by eval are strings.
6571 ASSERT(keys->get(i)->IsString());
6572 Handle<String> key(String::cast(keys->get(i)));
6573 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6574 }
6575 }
6576
6577 // Allocate a new context for the debug evaluation and set the extension
6578 // object build.
6579 Handle<Context> context =
6580 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6581 context->set_extension(*context_ext);
6582 // Copy any with contexts present and chain them in front of this context.
6583 context = CopyWithContextChain(frame_context, context);
6584
6585 // Wrap the evaluation statement in a new function compiled in the newly
6586 // created context. The function has one parameter which has to be called
6587 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006588 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006589 // function(arguments,__source__) {return eval(__source__);}
6590 static const char* source_str =
6591 "function(arguments,__source__){return eval(__source__);}";
6592 static const int source_str_length = strlen(source_str);
6593 Handle<String> function_source =
6594 Factory::NewStringFromAscii(Vector<const char>(source_str,
6595 source_str_length));
6596 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006597 Compiler::CompileEval(function_source,
6598 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006599 context->IsGlobalContext(),
6600 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006601 if (boilerplate.is_null()) return Failure::Exception();
6602 Handle<JSFunction> compiled_function =
6603 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6604
6605 // Invoke the result of the compilation to get the evaluation function.
6606 bool has_pending_exception;
6607 Handle<Object> receiver(frame->receiver());
6608 Handle<Object> evaluation_function =
6609 Execution::Call(compiled_function, receiver, 0, NULL,
6610 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006611 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006612
6613 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6614 function_context);
6615
6616 // Invoke the evaluation function and return the result.
6617 const int argc = 2;
6618 Object** argv[argc] = { arguments.location(),
6619 Handle<Object>::cast(source).location() };
6620 Handle<Object> result =
6621 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6622 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006623 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006624 return *result;
6625}
6626
6627
6628static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6629 HandleScope scope;
6630
6631 // Check the execution state and decode arguments frame and source to be
6632 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006633 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006634 Object* check_result = Runtime_CheckExecutionState(args);
6635 if (check_result->IsFailure()) return check_result;
6636 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006637 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6638
6639 // Handle the processing of break.
6640 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006641
6642 // Enter the top context from before the debugger was invoked.
6643 SaveContext save;
6644 SaveContext* top = &save;
6645 while (top != NULL && *top->context() == *Debug::debug_context()) {
6646 top = top->prev();
6647 }
6648 if (top != NULL) {
6649 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006650 }
6651
6652 // Get the global context now set to the top context from before the
6653 // debugger was invoked.
6654 Handle<Context> context = Top::global_context();
6655
6656 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006657 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006658 Handle<JSFunction>(Compiler::CompileEval(source,
6659 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006660 true,
6661 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006662 if (boilerplate.is_null()) return Failure::Exception();
6663 Handle<JSFunction> compiled_function =
6664 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6665 context));
6666
6667 // Invoke the result of the compilation to get the evaluation function.
6668 bool has_pending_exception;
6669 Handle<Object> receiver = Top::global();
6670 Handle<Object> result =
6671 Execution::Call(compiled_function, receiver, 0, NULL,
6672 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006673 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006674 return *result;
6675}
6676
6677
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006678static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6679 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006680 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006681
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006682 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006683 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006684
6685 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006686 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006687 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6688 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6689 // because using
6690 // instances->set(i, *GetScriptWrapper(script))
6691 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6692 // already have deferenced the instances handle.
6693 Handle<JSValue> wrapper = GetScriptWrapper(script);
6694 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006695 }
6696
6697 // Return result as a JS array.
6698 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6699 Handle<JSArray>::cast(result)->SetContent(*instances);
6700 return *result;
6701}
6702
6703
6704// Helper function used by Runtime_DebugReferencedBy below.
6705static int DebugReferencedBy(JSObject* target,
6706 Object* instance_filter, int max_references,
6707 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006708 JSFunction* arguments_function) {
6709 NoHandleAllocation ha;
6710 AssertNoAllocation no_alloc;
6711
6712 // Iterate the heap.
6713 int count = 0;
6714 JSObject* last = NULL;
6715 HeapIterator iterator;
6716 while (iterator.has_next() &&
6717 (max_references == 0 || count < max_references)) {
6718 // Only look at all JSObjects.
6719 HeapObject* heap_obj = iterator.next();
6720 if (heap_obj->IsJSObject()) {
6721 // Skip context extension objects and argument arrays as these are
6722 // checked in the context of functions using them.
6723 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006724 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006725 obj->map()->constructor() == arguments_function) {
6726 continue;
6727 }
6728
6729 // Check if the JS object has a reference to the object looked for.
6730 if (obj->ReferencesObject(target)) {
6731 // Check instance filter if supplied. This is normally used to avoid
6732 // references from mirror objects (see Runtime_IsInPrototypeChain).
6733 if (!instance_filter->IsUndefined()) {
6734 Object* V = obj;
6735 while (true) {
6736 Object* prototype = V->GetPrototype();
6737 if (prototype->IsNull()) {
6738 break;
6739 }
6740 if (instance_filter == prototype) {
6741 obj = NULL; // Don't add this object.
6742 break;
6743 }
6744 V = prototype;
6745 }
6746 }
6747
6748 if (obj != NULL) {
6749 // Valid reference found add to instance array if supplied an update
6750 // count.
6751 if (instances != NULL && count < instances_size) {
6752 instances->set(count, obj);
6753 }
6754 last = obj;
6755 count++;
6756 }
6757 }
6758 }
6759 }
6760
6761 // Check for circular reference only. This can happen when the object is only
6762 // referenced from mirrors and has a circular reference in which case the
6763 // object is not really alive and would have been garbage collected if not
6764 // referenced from the mirror.
6765 if (count == 1 && last == target) {
6766 count = 0;
6767 }
6768
6769 // Return the number of referencing objects found.
6770 return count;
6771}
6772
6773
6774// Scan the heap for objects with direct references to an object
6775// args[0]: the object to find references to
6776// args[1]: constructor function for instances to exclude (Mirror)
6777// args[2]: the the maximum number of objects to return
6778static Object* Runtime_DebugReferencedBy(Arguments args) {
6779 ASSERT(args.length() == 3);
6780
6781 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006782 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006783
6784 // Check parameters.
6785 CONVERT_CHECKED(JSObject, target, args[0]);
6786 Object* instance_filter = args[1];
6787 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6788 instance_filter->IsJSObject());
6789 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6790 RUNTIME_ASSERT(max_references >= 0);
6791
6792 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793 JSObject* arguments_boilerplate =
6794 Top::context()->global_context()->arguments_boilerplate();
6795 JSFunction* arguments_function =
6796 JSFunction::cast(arguments_boilerplate->map()->constructor());
6797
6798 // Get the number of referencing objects.
6799 int count;
6800 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006801 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006802
6803 // Allocate an array to hold the result.
6804 Object* object = Heap::AllocateFixedArray(count);
6805 if (object->IsFailure()) return object;
6806 FixedArray* instances = FixedArray::cast(object);
6807
6808 // Fill the referencing objects.
6809 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006810 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006811
6812 // Return result as JS array.
6813 Object* result =
6814 Heap::AllocateJSObject(
6815 Top::context()->global_context()->array_function());
6816 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6817 return result;
6818}
6819
6820
6821// Helper function used by Runtime_DebugConstructedBy below.
6822static int DebugConstructedBy(JSFunction* constructor, int max_references,
6823 FixedArray* instances, int instances_size) {
6824 AssertNoAllocation no_alloc;
6825
6826 // Iterate the heap.
6827 int count = 0;
6828 HeapIterator iterator;
6829 while (iterator.has_next() &&
6830 (max_references == 0 || count < max_references)) {
6831 // Only look at all JSObjects.
6832 HeapObject* heap_obj = iterator.next();
6833 if (heap_obj->IsJSObject()) {
6834 JSObject* obj = JSObject::cast(heap_obj);
6835 if (obj->map()->constructor() == constructor) {
6836 // Valid reference found add to instance array if supplied an update
6837 // count.
6838 if (instances != NULL && count < instances_size) {
6839 instances->set(count, obj);
6840 }
6841 count++;
6842 }
6843 }
6844 }
6845
6846 // Return the number of referencing objects found.
6847 return count;
6848}
6849
6850
6851// Scan the heap for objects constructed by a specific function.
6852// args[0]: the constructor to find instances of
6853// args[1]: the the maximum number of objects to return
6854static Object* Runtime_DebugConstructedBy(Arguments args) {
6855 ASSERT(args.length() == 2);
6856
6857 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006858 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006859
6860 // Check parameters.
6861 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6862 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6863 RUNTIME_ASSERT(max_references >= 0);
6864
6865 // Get the number of referencing objects.
6866 int count;
6867 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6868
6869 // Allocate an array to hold the result.
6870 Object* object = Heap::AllocateFixedArray(count);
6871 if (object->IsFailure()) return object;
6872 FixedArray* instances = FixedArray::cast(object);
6873
6874 // Fill the referencing objects.
6875 count = DebugConstructedBy(constructor, max_references, instances, count);
6876
6877 // Return result as JS array.
6878 Object* result =
6879 Heap::AllocateJSObject(
6880 Top::context()->global_context()->array_function());
6881 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6882 return result;
6883}
6884
6885
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006886// Find the effective prototype object as returned by __proto__.
6887// args[0]: the object to find the prototype for.
6888static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006889 ASSERT(args.length() == 1);
6890
6891 CONVERT_CHECKED(JSObject, obj, args[0]);
6892
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006893 // Use the __proto__ accessor.
6894 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006895}
6896
6897
6898static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006899 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006900 CPU::DebugBreak();
6901 return Heap::undefined_value();
6902}
6903
6904
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006905static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6906#ifdef DEBUG
6907 HandleScope scope;
6908 ASSERT(args.length() == 1);
6909 // Get the function and make sure it is compiled.
6910 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6911 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6912 return Failure::Exception();
6913 }
6914 func->code()->PrintLn();
6915#endif // DEBUG
6916 return Heap::undefined_value();
6917}
ager@chromium.org9085a012009-05-11 19:22:57 +00006918
6919
6920static Object* Runtime_FunctionGetInferredName(Arguments args) {
6921 NoHandleAllocation ha;
6922 ASSERT(args.length() == 1);
6923
6924 CONVERT_CHECKED(JSFunction, f, args[0]);
6925 return f->shared()->inferred_name();
6926}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006927#endif // ENABLE_DEBUGGER_SUPPORT
6928
6929
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006930// Finds the script object from the script data. NOTE: This operation uses
6931// heap traversal to find the function generated for the source position
6932// for the requested break point. For lazily compiled functions several heap
6933// traversals might be required rendering this operation as a rather slow
6934// operation. However for setting break points which is normally done through
6935// some kind of user interaction the performance is not crucial.
6936static Handle<Object> Runtime_GetScriptFromScriptName(
6937 Handle<String> script_name) {
6938 // Scan the heap for Script objects to find the script with the requested
6939 // script data.
6940 Handle<Script> script;
6941 HeapIterator iterator;
6942 while (script.is_null() && iterator.has_next()) {
6943 HeapObject* obj = iterator.next();
6944 // If a script is found check if it has the script data requested.
6945 if (obj->IsScript()) {
6946 if (Script::cast(obj)->name()->IsString()) {
6947 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6948 script = Handle<Script>(Script::cast(obj));
6949 }
6950 }
6951 }
6952 }
6953
6954 // If no script with the requested script data is found return undefined.
6955 if (script.is_null()) return Factory::undefined_value();
6956
6957 // Return the script found.
6958 return GetScriptWrapper(script);
6959}
6960
6961
6962// Get the script object from script data. NOTE: Regarding performance
6963// see the NOTE for GetScriptFromScriptData.
6964// args[0]: script data for the script to find the source for
6965static Object* Runtime_GetScript(Arguments args) {
6966 HandleScope scope;
6967
6968 ASSERT(args.length() == 1);
6969
6970 CONVERT_CHECKED(String, script_name, args[0]);
6971
6972 // Find the requested script.
6973 Handle<Object> result =
6974 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
6975 return *result;
6976}
6977
6978
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006979static Object* Runtime_Abort(Arguments args) {
6980 ASSERT(args.length() == 2);
6981 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
6982 Smi::cast(args[1])->value());
6983 Top::PrintStack();
6984 OS::Abort();
6985 UNREACHABLE();
6986 return NULL;
6987}
6988
6989
kasper.lund44510672008-07-25 07:37:58 +00006990#ifdef DEBUG
6991// ListNatives is ONLY used by the fuzz-natives.js in debug mode
6992// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006993static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006994 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006995 HandleScope scope;
6996 Handle<JSArray> result = Factory::NewJSArray(0);
6997 int index = 0;
6998#define ADD_ENTRY(Name, argc) \
6999 { \
7000 HandleScope inner; \
7001 Handle<String> name = \
7002 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7003 Handle<JSArray> pair = Factory::NewJSArray(0); \
7004 SetElement(pair, 0, name); \
7005 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7006 SetElement(result, index++, pair); \
7007 }
7008 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7009#undef ADD_ENTRY
7010 return *result;
7011}
kasper.lund44510672008-07-25 07:37:58 +00007012#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007013
7014
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007015static Object* Runtime_Log(Arguments args) {
7016 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007017 CONVERT_CHECKED(String, format, args[0]);
7018 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007019 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007020 Logger::LogRuntime(chars, elms);
7021 return Heap::undefined_value();
7022}
7023
7024
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007025static Object* Runtime_IS_VAR(Arguments args) {
7026 UNREACHABLE(); // implemented as macro in the parser
7027 return NULL;
7028}
7029
7030
7031// ----------------------------------------------------------------------------
7032// Implementation of Runtime
7033
7034#define F(name, nargs) \
7035 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7036 static_cast<int>(Runtime::k##name) },
7037
7038static Runtime::Function Runtime_functions[] = {
7039 RUNTIME_FUNCTION_LIST(F)
7040 { NULL, NULL, NULL, 0, -1 }
7041};
7042
7043#undef F
7044
7045
7046Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7047 ASSERT(0 <= fid && fid < kNofFunctions);
7048 return &Runtime_functions[fid];
7049}
7050
7051
7052Runtime::Function* Runtime::FunctionForName(const char* name) {
7053 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7054 if (strcmp(f->name, name) == 0) {
7055 return f;
7056 }
7057 }
7058 return NULL;
7059}
7060
7061
7062void Runtime::PerformGC(Object* result) {
7063 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007064 if (failure->IsRetryAfterGC()) {
7065 // Try to do a garbage collection; ignore it if it fails. The C
7066 // entry stub will throw an out-of-memory exception in that case.
7067 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7068 } else {
7069 // Handle last resort GC and make sure to allow future allocations
7070 // to grow the heap without causing GCs (if possible).
7071 Counters::gc_last_resort_from_js.Increment();
7072 Heap::CollectAllGarbage();
7073 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007074}
7075
7076
7077} } // namespace v8::internal