blob: 78be5129250a409143fabe98309624e29620f0c2 [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
ager@chromium.orge2902be2009-06-08 12:21:35 +00002783Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2784 Handle<Object> key) {
2785 HandleScope scope;
2786
2787 // Check if the given key is an array index.
2788 uint32_t index;
2789 if (Array::IndexFromObject(*key, &index)) {
2790 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2791 // characters of a string using [] notation. In the case of a
2792 // String object we just need to redirect the deletion to the
2793 // underlying string if the index is in range. Since the
2794 // underlying string does nothing with the deletion, we can ignore
2795 // such deletions.
2796 if (js_object->IsStringObjectWithCharacterAt(index)) {
2797 return Heap::true_value();
2798 }
2799
2800 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2801 }
2802
2803 Handle<String> key_string;
2804 if (key->IsString()) {
2805 key_string = Handle<String>::cast(key);
2806 } else {
2807 // Call-back into JavaScript to convert the key to a string.
2808 bool has_pending_exception = false;
2809 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2810 if (has_pending_exception) return Failure::Exception();
2811 key_string = Handle<String>::cast(converted);
2812 }
2813
2814 key_string->TryFlattenIfNotFlat();
2815 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2816}
2817
2818
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002819static Object* Runtime_SetProperty(Arguments args) {
2820 NoHandleAllocation ha;
2821 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2822
2823 Handle<Object> object = args.at<Object>(0);
2824 Handle<Object> key = args.at<Object>(1);
2825 Handle<Object> value = args.at<Object>(2);
2826
2827 // Compute attributes.
2828 PropertyAttributes attributes = NONE;
2829 if (args.length() == 4) {
2830 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002831 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002832 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002833 RUNTIME_ASSERT(
2834 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2835 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002836 }
2837 return Runtime::SetObjectProperty(object, key, value, attributes);
2838}
2839
2840
2841// Set a local property, even if it is READ_ONLY. If the property does not
2842// exist, it will be added with attributes NONE.
2843static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2844 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002845 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002846 CONVERT_CHECKED(JSObject, object, args[0]);
2847 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002848 // Compute attributes.
2849 PropertyAttributes attributes = NONE;
2850 if (args.length() == 4) {
2851 CONVERT_CHECKED(Smi, value_obj, args[3]);
2852 int unchecked_value = value_obj->value();
2853 // Only attribute bits should be set.
2854 RUNTIME_ASSERT(
2855 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2856 attributes = static_cast<PropertyAttributes>(unchecked_value);
2857 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002858
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002859 return object->
2860 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002861}
2862
2863
2864static Object* Runtime_DeleteProperty(Arguments args) {
2865 NoHandleAllocation ha;
2866 ASSERT(args.length() == 2);
2867
2868 CONVERT_CHECKED(JSObject, object, args[0]);
2869 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002870 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002871}
2872
2873
ager@chromium.org9085a012009-05-11 19:22:57 +00002874static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2875 Handle<String> key) {
2876 if (object->HasLocalProperty(*key)) return Heap::true_value();
2877 // Handle hidden prototypes. If there's a hidden prototype above this thing
2878 // then we have to check it for properties, because they are supposed to
2879 // look like they are on this object.
2880 Handle<Object> proto(object->GetPrototype());
2881 if (proto->IsJSObject() &&
2882 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2883 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2884 }
2885 return Heap::false_value();
2886}
2887
2888
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002889static Object* Runtime_HasLocalProperty(Arguments args) {
2890 NoHandleAllocation ha;
2891 ASSERT(args.length() == 2);
2892 CONVERT_CHECKED(String, key, args[1]);
2893
ager@chromium.org9085a012009-05-11 19:22:57 +00002894 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002895 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002896 if (obj->IsJSObject()) {
2897 JSObject* object = JSObject::cast(obj);
2898 // Fast case - no interceptors.
2899 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2900 // Slow case. Either it's not there or we have an interceptor. We should
2901 // have handles for this kind of deal.
2902 HandleScope scope;
2903 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2904 Handle<String>(key));
2905 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002906 // Well, there is one exception: Handle [] on strings.
2907 uint32_t index;
2908 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002909 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002910 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002911 return Heap::true_value();
2912 }
2913 }
2914 return Heap::false_value();
2915}
2916
2917
2918static Object* Runtime_HasProperty(Arguments args) {
2919 NoHandleAllocation na;
2920 ASSERT(args.length() == 2);
2921
2922 // Only JS objects can have properties.
2923 if (args[0]->IsJSObject()) {
2924 JSObject* object = JSObject::cast(args[0]);
2925 CONVERT_CHECKED(String, key, args[1]);
2926 if (object->HasProperty(key)) return Heap::true_value();
2927 }
2928 return Heap::false_value();
2929}
2930
2931
2932static Object* Runtime_HasElement(Arguments args) {
2933 NoHandleAllocation na;
2934 ASSERT(args.length() == 2);
2935
2936 // Only JS objects can have elements.
2937 if (args[0]->IsJSObject()) {
2938 JSObject* object = JSObject::cast(args[0]);
2939 CONVERT_CHECKED(Smi, index_obj, args[1]);
2940 uint32_t index = index_obj->value();
2941 if (object->HasElement(index)) return Heap::true_value();
2942 }
2943 return Heap::false_value();
2944}
2945
2946
2947static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2948 NoHandleAllocation ha;
2949 ASSERT(args.length() == 2);
2950
2951 CONVERT_CHECKED(JSObject, object, args[0]);
2952 CONVERT_CHECKED(String, key, args[1]);
2953
2954 uint32_t index;
2955 if (key->AsArrayIndex(&index)) {
2956 return Heap::ToBoolean(object->HasElement(index));
2957 }
2958
ager@chromium.org870a0b62008-11-04 11:43:05 +00002959 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2960 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002961}
2962
2963
2964static Object* Runtime_GetPropertyNames(Arguments args) {
2965 HandleScope scope;
2966 ASSERT(args.length() == 1);
2967
2968 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2969 Handle<JSObject> object(raw_object);
2970 return *GetKeysFor(object);
2971}
2972
2973
2974// Returns either a FixedArray as Runtime_GetPropertyNames,
2975// or, if the given object has an enum cache that contains
2976// all enumerable properties of the object and its prototypes
2977// have none, the map of the object. This is used to speed up
2978// the check for deletions during a for-in.
2979static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2980 ASSERT(args.length() == 1);
2981
2982 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2983
2984 if (raw_object->IsSimpleEnum()) return raw_object->map();
2985
2986 HandleScope scope;
2987 Handle<JSObject> object(raw_object);
2988 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2989
2990 // Test again, since cache may have been built by preceding call.
2991 if (object->IsSimpleEnum()) return object->map();
2992
2993 return *content;
2994}
2995
2996
2997static Object* Runtime_GetArgumentsProperty(Arguments args) {
2998 NoHandleAllocation ha;
2999 ASSERT(args.length() == 1);
3000
3001 // Compute the frame holding the arguments.
3002 JavaScriptFrameIterator it;
3003 it.AdvanceToArgumentsFrame();
3004 JavaScriptFrame* frame = it.frame();
3005
3006 // Get the actual number of provided arguments.
3007 const uint32_t n = frame->GetProvidedParametersCount();
3008
3009 // Try to convert the key to an index. If successful and within
3010 // index return the the argument from the frame.
3011 uint32_t index;
3012 if (Array::IndexFromObject(args[0], &index) && index < n) {
3013 return frame->GetParameter(index);
3014 }
3015
3016 // Convert the key to a string.
3017 HandleScope scope;
3018 bool exception = false;
3019 Handle<Object> converted =
3020 Execution::ToString(args.at<Object>(0), &exception);
3021 if (exception) return Failure::Exception();
3022 Handle<String> key = Handle<String>::cast(converted);
3023
3024 // Try to convert the string key into an array index.
3025 if (key->AsArrayIndex(&index)) {
3026 if (index < n) {
3027 return frame->GetParameter(index);
3028 } else {
3029 return Top::initial_object_prototype()->GetElement(index);
3030 }
3031 }
3032
3033 // Handle special arguments properties.
3034 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3035 if (key->Equals(Heap::callee_symbol())) return frame->function();
3036
3037 // Lookup in the initial Object.prototype object.
3038 return Top::initial_object_prototype()->GetProperty(*key);
3039}
3040
3041
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003042static Object* Runtime_ToFastProperties(Arguments args) {
3043 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003044 Handle<Object> object = args.at<Object>(0);
3045 if (object->IsJSObject()) {
3046 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3047 js_object->TransformToFastProperties(0);
3048 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003049 return *object;
3050}
3051
3052
3053static Object* Runtime_ToSlowProperties(Arguments args) {
3054 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003055 Handle<Object> object = args.at<Object>(0);
3056 if (object->IsJSObject()) {
3057 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3058 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
3059 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003060 return *object;
3061}
3062
3063
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003064static Object* Runtime_ToBool(Arguments args) {
3065 NoHandleAllocation ha;
3066 ASSERT(args.length() == 1);
3067
3068 return args[0]->ToBoolean();
3069}
3070
3071
3072// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3073// Possible optimizations: put the type string into the oddballs.
3074static Object* Runtime_Typeof(Arguments args) {
3075 NoHandleAllocation ha;
3076
3077 Object* obj = args[0];
3078 if (obj->IsNumber()) return Heap::number_symbol();
3079 HeapObject* heap_obj = HeapObject::cast(obj);
3080
3081 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003082 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003083
3084 InstanceType instance_type = heap_obj->map()->instance_type();
3085 if (instance_type < FIRST_NONSTRING_TYPE) {
3086 return Heap::string_symbol();
3087 }
3088
3089 switch (instance_type) {
3090 case ODDBALL_TYPE:
3091 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3092 return Heap::boolean_symbol();
3093 }
3094 if (heap_obj->IsNull()) {
3095 return Heap::object_symbol();
3096 }
3097 ASSERT(heap_obj->IsUndefined());
3098 return Heap::undefined_symbol();
3099 case JS_FUNCTION_TYPE:
3100 return Heap::function_symbol();
3101 default:
3102 // For any kind of object not handled above, the spec rule for
3103 // host objects gives that it is okay to return "object"
3104 return Heap::object_symbol();
3105 }
3106}
3107
3108
3109static Object* Runtime_StringToNumber(Arguments args) {
3110 NoHandleAllocation ha;
3111 ASSERT(args.length() == 1);
3112 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003113 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003114 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3115}
3116
3117
3118static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3119 NoHandleAllocation ha;
3120 ASSERT(args.length() == 1);
3121
3122 CONVERT_CHECKED(JSArray, codes, args[0]);
3123 int length = Smi::cast(codes->length())->value();
3124
3125 // Check if the string can be ASCII.
3126 int i;
3127 for (i = 0; i < length; i++) {
3128 Object* element = codes->GetElement(i);
3129 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3130 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3131 break;
3132 }
3133
3134 Object* object = NULL;
3135 if (i == length) { // The string is ASCII.
3136 object = Heap::AllocateRawAsciiString(length);
3137 } else { // The string is not ASCII.
3138 object = Heap::AllocateRawTwoByteString(length);
3139 }
3140
3141 if (object->IsFailure()) return object;
3142 String* result = String::cast(object);
3143 for (int i = 0; i < length; i++) {
3144 Object* element = codes->GetElement(i);
3145 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003146 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003147 }
3148 return result;
3149}
3150
3151
3152// kNotEscaped is generated by the following:
3153//
3154// #!/bin/perl
3155// for (my $i = 0; $i < 256; $i++) {
3156// print "\n" if $i % 16 == 0;
3157// my $c = chr($i);
3158// my $escaped = 1;
3159// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3160// print $escaped ? "0, " : "1, ";
3161// }
3162
3163
3164static bool IsNotEscaped(uint16_t character) {
3165 // Only for 8 bit characters, the rest are always escaped (in a different way)
3166 ASSERT(character < 256);
3167 static const char kNotEscaped[256] = {
3168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3171 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3172 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3173 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3174 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3175 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3182 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3183 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3184 };
3185 return kNotEscaped[character] != 0;
3186}
3187
3188
3189static Object* Runtime_URIEscape(Arguments args) {
3190 const char hex_chars[] = "0123456789ABCDEF";
3191 NoHandleAllocation ha;
3192 ASSERT(args.length() == 1);
3193 CONVERT_CHECKED(String, source, args[0]);
3194
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003195 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003196
3197 int escaped_length = 0;
3198 int length = source->length();
3199 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003200 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003201 buffer->Reset(source);
3202 while (buffer->has_more()) {
3203 uint16_t character = buffer->GetNext();
3204 if (character >= 256) {
3205 escaped_length += 6;
3206 } else if (IsNotEscaped(character)) {
3207 escaped_length++;
3208 } else {
3209 escaped_length += 3;
3210 }
3211 // We don't allow strings that are longer than Smi range.
3212 if (!Smi::IsValid(escaped_length)) {
3213 Top::context()->mark_out_of_memory();
3214 return Failure::OutOfMemoryException();
3215 }
3216 }
3217 }
3218 // No length change implies no change. Return original string if no change.
3219 if (escaped_length == length) {
3220 return source;
3221 }
3222 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3223 if (o->IsFailure()) return o;
3224 String* destination = String::cast(o);
3225 int dest_position = 0;
3226
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003227 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003228 buffer->Rewind();
3229 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003230 uint16_t chr = buffer->GetNext();
3231 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003232 destination->Set(dest_position, '%');
3233 destination->Set(dest_position+1, 'u');
3234 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3235 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3236 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3237 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003238 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003239 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003240 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241 dest_position++;
3242 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003243 destination->Set(dest_position, '%');
3244 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3245 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003246 dest_position += 3;
3247 }
3248 }
3249 return destination;
3250}
3251
3252
3253static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3254 static const signed char kHexValue['g'] = {
3255 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3256 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3257 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3258 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3259 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3260 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3261 -1, 10, 11, 12, 13, 14, 15 };
3262
3263 if (character1 > 'f') return -1;
3264 int hi = kHexValue[character1];
3265 if (hi == -1) return -1;
3266 if (character2 > 'f') return -1;
3267 int lo = kHexValue[character2];
3268 if (lo == -1) return -1;
3269 return (hi << 4) + lo;
3270}
3271
3272
ager@chromium.org870a0b62008-11-04 11:43:05 +00003273static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003274 int i,
3275 int length,
3276 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003277 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003278 int32_t hi = 0;
3279 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280 if (character == '%' &&
3281 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003282 source->Get(i + 1) == 'u' &&
3283 (hi = TwoDigitHex(source->Get(i + 2),
3284 source->Get(i + 3))) != -1 &&
3285 (lo = TwoDigitHex(source->Get(i + 4),
3286 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003287 *step = 6;
3288 return (hi << 8) + lo;
3289 } else if (character == '%' &&
3290 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003291 (lo = TwoDigitHex(source->Get(i + 1),
3292 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003293 *step = 3;
3294 return lo;
3295 } else {
3296 *step = 1;
3297 return character;
3298 }
3299}
3300
3301
3302static Object* Runtime_URIUnescape(Arguments args) {
3303 NoHandleAllocation ha;
3304 ASSERT(args.length() == 1);
3305 CONVERT_CHECKED(String, source, args[0]);
3306
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003307 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003308
3309 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003310 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003311
3312 int unescaped_length = 0;
3313 for (int i = 0; i < length; unescaped_length++) {
3314 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003315 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003316 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003317 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003318 i += step;
3319 }
3320
3321 // No length change implies no change. Return original string if no change.
3322 if (unescaped_length == length)
3323 return source;
3324
3325 Object* o = ascii ?
3326 Heap::AllocateRawAsciiString(unescaped_length) :
3327 Heap::AllocateRawTwoByteString(unescaped_length);
3328 if (o->IsFailure()) return o;
3329 String* destination = String::cast(o);
3330
3331 int dest_position = 0;
3332 for (int i = 0; i < length; dest_position++) {
3333 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003334 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003335 i += step;
3336 }
3337 return destination;
3338}
3339
3340
3341static Object* Runtime_StringParseInt(Arguments args) {
3342 NoHandleAllocation ha;
3343
3344 CONVERT_CHECKED(String, s, args[0]);
3345 CONVERT_DOUBLE_CHECKED(n, args[1]);
3346 int radix = FastD2I(n);
3347
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003348 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003349
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003350 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003351 int i;
3352
3353 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003354 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003355 if (i == len) return Heap::nan_value();
3356
3357 // Compute the sign (default to +).
3358 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003359 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003360 sign = -1;
3361 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003362 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003363 i++;
3364 }
3365
3366 // Compute the radix if 0.
3367 if (radix == 0) {
3368 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003369 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003370 radix = 8;
3371 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003372 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003373 if (c == 'x' || c == 'X') {
3374 radix = 16;
3375 i += 2;
3376 }
3377 }
3378 }
3379 } else if (radix == 16) {
3380 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003381 if (i + 1 < len && s->Get(i) == '0') {
3382 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003383 if (c == 'x' || c == 'X') i += 2;
3384 }
3385 }
3386
3387 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3388 double value;
3389 int end_index = StringToInt(s, i, radix, &value);
3390 if (end_index != i) {
3391 return Heap::NumberFromDouble(sign * value);
3392 }
3393 return Heap::nan_value();
3394}
3395
3396
3397static Object* Runtime_StringParseFloat(Arguments args) {
3398 NoHandleAllocation ha;
3399 CONVERT_CHECKED(String, str, args[0]);
3400
3401 // ECMA-262 section 15.1.2.3, empty string is NaN
3402 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3403
3404 // Create a number object from the value.
3405 return Heap::NumberFromDouble(value);
3406}
3407
3408
3409static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3410static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3411
3412
3413template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003414static Object* ConvertCaseHelper(String* s,
3415 int length,
3416 int input_string_length,
3417 unibrow::Mapping<Converter, 128>* mapping) {
3418 // We try this twice, once with the assumption that the result is no longer
3419 // than the input and, if that assumption breaks, again with the exact
3420 // length. This may not be pretty, but it is nicer than what was here before
3421 // and I hereby claim my vaffel-is.
3422 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003423 // Allocate the resulting string.
3424 //
3425 // NOTE: This assumes that the upper/lower case of an ascii
3426 // character is also ascii. This is currently the case, but it
3427 // might break in the future if we implement more context and locale
3428 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003429 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003430 ? Heap::AllocateRawAsciiString(length)
3431 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003432 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003433 String* result = String::cast(o);
3434 bool has_changed_character = false;
3435
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003436 // Convert all characters to upper case, assuming that they will fit
3437 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003438 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003439 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003440 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003441 // We can assume that the string is not empty
3442 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003443 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003444 bool has_next = buffer->has_more();
3445 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003446 int char_length = mapping->get(current, next, chars);
3447 if (char_length == 0) {
3448 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003449 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003450 i++;
3451 } else if (char_length == 1) {
3452 // Common case: converting the letter resulted in one character.
3453 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003454 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003455 has_changed_character = true;
3456 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003457 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003458 // We've assumed that the result would be as long as the
3459 // input but here is a character that converts to several
3460 // characters. No matter, we calculate the exact length
3461 // of the result and try the whole thing again.
3462 //
3463 // Note that this leaves room for optimization. We could just
3464 // memcpy what we already have to the result string. Also,
3465 // the result string is the last object allocated we could
3466 // "realloc" it and probably, in the vast majority of cases,
3467 // extend the existing string to be able to hold the full
3468 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003469 int next_length = 0;
3470 if (has_next) {
3471 next_length = mapping->get(next, 0, chars);
3472 if (next_length == 0) next_length = 1;
3473 }
3474 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003475 while (buffer->has_more()) {
3476 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003477 // NOTE: we use 0 as the next character here because, while
3478 // the next character may affect what a character converts to,
3479 // it does not in any case affect the length of what it convert
3480 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003481 int char_length = mapping->get(current, 0, chars);
3482 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003483 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003484 if (current_length > Smi::kMaxValue) {
3485 Top::context()->mark_out_of_memory();
3486 return Failure::OutOfMemoryException();
3487 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003488 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003489 // Try again with the real length.
3490 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003491 } else {
3492 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003493 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003494 i++;
3495 }
3496 has_changed_character = true;
3497 }
3498 current = next;
3499 }
3500 if (has_changed_character) {
3501 return result;
3502 } else {
3503 // If we didn't actually change anything in doing the conversion
3504 // we simple return the result and let the converted string
3505 // become garbage; there is no reason to keep two identical strings
3506 // alive.
3507 return s;
3508 }
3509}
3510
3511
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003512template <class Converter>
3513static Object* ConvertCase(Arguments args,
3514 unibrow::Mapping<Converter, 128>* mapping) {
3515 NoHandleAllocation ha;
3516
3517 CONVERT_CHECKED(String, s, args[0]);
3518 s->TryFlattenIfNotFlat();
3519
3520 int input_string_length = s->length();
3521 // Assume that the string is not empty; we need this assumption later
3522 if (input_string_length == 0) return s;
3523 int length = input_string_length;
3524
3525 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3526 if (answer->IsSmi()) {
3527 // Retry with correct length.
3528 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3529 }
3530 return answer; // This may be a failure.
3531}
3532
3533
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003534static Object* Runtime_StringToLowerCase(Arguments args) {
3535 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3536}
3537
3538
3539static Object* Runtime_StringToUpperCase(Arguments args) {
3540 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3541}
3542
3543
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003544bool Runtime::IsUpperCaseChar(uint16_t ch) {
3545 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3546 int char_length = to_upper_mapping.get(ch, 0, chars);
3547 return char_length == 0;
3548}
3549
3550
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003551static Object* Runtime_NumberToString(Arguments args) {
3552 NoHandleAllocation ha;
3553 ASSERT(args.length() == 1);
3554
3555 Object* number = args[0];
3556 RUNTIME_ASSERT(number->IsNumber());
3557
3558 Object* cached = Heap::GetNumberStringCache(number);
3559 if (cached != Heap::undefined_value()) {
3560 return cached;
3561 }
3562
3563 char arr[100];
3564 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3565 const char* str;
3566 if (number->IsSmi()) {
3567 int num = Smi::cast(number)->value();
3568 str = IntToCString(num, buffer);
3569 } else {
3570 double num = HeapNumber::cast(number)->value();
3571 str = DoubleToCString(num, buffer);
3572 }
3573 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3574
3575 if (!result->IsFailure()) {
3576 Heap::SetNumberStringCache(number, String::cast(result));
3577 }
3578 return result;
3579}
3580
3581
3582static Object* Runtime_NumberToInteger(Arguments args) {
3583 NoHandleAllocation ha;
3584 ASSERT(args.length() == 1);
3585
3586 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003587 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003588 CONVERT_DOUBLE_CHECKED(number, obj);
3589 return Heap::NumberFromDouble(DoubleToInteger(number));
3590}
3591
3592
3593static Object* Runtime_NumberToJSUint32(Arguments args) {
3594 NoHandleAllocation ha;
3595 ASSERT(args.length() == 1);
3596
3597 Object* obj = args[0];
3598 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3599 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3600 return Heap::NumberFromUint32(number);
3601}
3602
3603
3604static Object* Runtime_NumberToJSInt32(Arguments args) {
3605 NoHandleAllocation ha;
3606 ASSERT(args.length() == 1);
3607
3608 Object* obj = args[0];
3609 if (obj->IsSmi()) return obj;
3610 CONVERT_DOUBLE_CHECKED(number, obj);
3611 return Heap::NumberFromInt32(DoubleToInt32(number));
3612}
3613
3614
ager@chromium.org870a0b62008-11-04 11:43:05 +00003615// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3616// a small integer.
3617static Object* Runtime_NumberToSmi(Arguments args) {
3618 NoHandleAllocation ha;
3619 ASSERT(args.length() == 1);
3620
3621 Object* obj = args[0];
3622 if (obj->IsSmi()) {
3623 return obj;
3624 }
3625 if (obj->IsHeapNumber()) {
3626 double value = HeapNumber::cast(obj)->value();
3627 int int_value = FastD2I(value);
3628 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3629 return Smi::FromInt(int_value);
3630 }
3631 }
3632 return Heap::nan_value();
3633}
3634
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003635
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003636static Object* Runtime_NumberAdd(Arguments args) {
3637 NoHandleAllocation ha;
3638 ASSERT(args.length() == 2);
3639
3640 CONVERT_DOUBLE_CHECKED(x, args[0]);
3641 CONVERT_DOUBLE_CHECKED(y, args[1]);
3642 return Heap::AllocateHeapNumber(x + y);
3643}
3644
3645
3646static Object* Runtime_NumberSub(Arguments args) {
3647 NoHandleAllocation ha;
3648 ASSERT(args.length() == 2);
3649
3650 CONVERT_DOUBLE_CHECKED(x, args[0]);
3651 CONVERT_DOUBLE_CHECKED(y, args[1]);
3652 return Heap::AllocateHeapNumber(x - y);
3653}
3654
3655
3656static Object* Runtime_NumberMul(Arguments args) {
3657 NoHandleAllocation ha;
3658 ASSERT(args.length() == 2);
3659
3660 CONVERT_DOUBLE_CHECKED(x, args[0]);
3661 CONVERT_DOUBLE_CHECKED(y, args[1]);
3662 return Heap::AllocateHeapNumber(x * y);
3663}
3664
3665
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003666static Object* Runtime_NumberUnaryMinus(Arguments args) {
3667 NoHandleAllocation ha;
3668 ASSERT(args.length() == 1);
3669
3670 CONVERT_DOUBLE_CHECKED(x, args[0]);
3671 return Heap::AllocateHeapNumber(-x);
3672}
3673
3674
3675static Object* Runtime_NumberDiv(Arguments args) {
3676 NoHandleAllocation ha;
3677 ASSERT(args.length() == 2);
3678
3679 CONVERT_DOUBLE_CHECKED(x, args[0]);
3680 CONVERT_DOUBLE_CHECKED(y, args[1]);
3681 return Heap::NewNumberFromDouble(x / y);
3682}
3683
3684
3685static Object* Runtime_NumberMod(Arguments args) {
3686 NoHandleAllocation ha;
3687 ASSERT(args.length() == 2);
3688
3689 CONVERT_DOUBLE_CHECKED(x, args[0]);
3690 CONVERT_DOUBLE_CHECKED(y, args[1]);
3691
3692#ifdef WIN32
3693 // Workaround MS fmod bugs. ECMA-262 says:
3694 // dividend is finite and divisor is an infinity => result equals dividend
3695 // dividend is a zero and divisor is nonzero finite => result equals dividend
3696 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3697 !(x == 0 && (y != 0 && isfinite(y))))
3698#endif
3699 x = fmod(x, y);
3700 // NewNumberFromDouble may return a Smi instead of a Number object
3701 return Heap::NewNumberFromDouble(x);
3702}
3703
3704
3705static Object* Runtime_StringAdd(Arguments args) {
3706 NoHandleAllocation ha;
3707 ASSERT(args.length() == 2);
3708
3709 CONVERT_CHECKED(String, str1, args[0]);
3710 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003711 int len1 = str1->length();
3712 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003713 if (len1 == 0) return str2;
3714 if (len2 == 0) return str1;
3715 int length_sum = len1 + len2;
3716 // Make sure that an out of memory exception is thrown if the length
3717 // of the new cons string is too large to fit in a Smi.
3718 if (length_sum > Smi::kMaxValue || length_sum < 0) {
3719 Top::context()->mark_out_of_memory();
3720 return Failure::OutOfMemoryException();
3721 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003722 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723}
3724
3725
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003726template<typename sinkchar>
3727static inline void StringBuilderConcatHelper(String* special,
3728 sinkchar* sink,
3729 FixedArray* fixed_array,
3730 int array_length) {
3731 int position = 0;
3732 for (int i = 0; i < array_length; i++) {
3733 Object* element = fixed_array->get(i);
3734 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003735 int encoded_slice = Smi::cast(element)->value();
3736 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3737 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003738 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003739 sink + position,
3740 pos,
3741 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003742 position += len;
3743 } else {
3744 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003745 int element_length = string->length();
3746 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003747 position += element_length;
3748 }
3749 }
3750}
3751
3752
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003753static Object* Runtime_StringBuilderConcat(Arguments args) {
3754 NoHandleAllocation ha;
3755 ASSERT(args.length() == 2);
3756 CONVERT_CHECKED(JSArray, array, args[0]);
3757 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003758 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003759 Object* smi_array_length = array->length();
3760 if (!smi_array_length->IsSmi()) {
3761 Top::context()->mark_out_of_memory();
3762 return Failure::OutOfMemoryException();
3763 }
3764 int array_length = Smi::cast(smi_array_length)->value();
3765 if (!array->HasFastElements()) {
3766 return Top::Throw(Heap::illegal_argument_symbol());
3767 }
3768 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003769 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003770 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003771 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003772
3773 if (array_length == 0) {
3774 return Heap::empty_string();
3775 } else if (array_length == 1) {
3776 Object* first = fixed_array->get(0);
3777 if (first->IsString()) return first;
3778 }
3779
ager@chromium.org5ec48922009-05-05 07:25:34 +00003780 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003781 int position = 0;
3782 for (int i = 0; i < array_length; i++) {
3783 Object* elt = fixed_array->get(i);
3784 if (elt->IsSmi()) {
3785 int len = Smi::cast(elt)->value();
3786 int pos = len >> 11;
3787 len &= 0x7ff;
3788 if (pos + len > special_length) {
3789 return Top::Throw(Heap::illegal_argument_symbol());
3790 }
3791 position += len;
3792 } else if (elt->IsString()) {
3793 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003794 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003795 if (!Smi::IsValid(element_length + position)) {
3796 Top::context()->mark_out_of_memory();
3797 return Failure::OutOfMemoryException();
3798 }
3799 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003800 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003801 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003802 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003803 } else {
3804 return Top::Throw(Heap::illegal_argument_symbol());
3805 }
3806 }
3807
3808 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003809 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003810
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003811 if (ascii) {
3812 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003813 if (object->IsFailure()) return object;
3814 SeqAsciiString* answer = SeqAsciiString::cast(object);
3815 StringBuilderConcatHelper(special,
3816 answer->GetChars(),
3817 fixed_array,
3818 array_length);
3819 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003820 } else {
3821 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003822 if (object->IsFailure()) return object;
3823 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3824 StringBuilderConcatHelper(special,
3825 answer->GetChars(),
3826 fixed_array,
3827 array_length);
3828 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003829 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003830}
3831
3832
3833static Object* Runtime_NumberOr(Arguments args) {
3834 NoHandleAllocation ha;
3835 ASSERT(args.length() == 2);
3836
3837 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3838 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3839 return Heap::NumberFromInt32(x | y);
3840}
3841
3842
3843static Object* Runtime_NumberAnd(Arguments args) {
3844 NoHandleAllocation ha;
3845 ASSERT(args.length() == 2);
3846
3847 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3848 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3849 return Heap::NumberFromInt32(x & y);
3850}
3851
3852
3853static Object* Runtime_NumberXor(Arguments args) {
3854 NoHandleAllocation ha;
3855 ASSERT(args.length() == 2);
3856
3857 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3858 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3859 return Heap::NumberFromInt32(x ^ y);
3860}
3861
3862
3863static Object* Runtime_NumberNot(Arguments args) {
3864 NoHandleAllocation ha;
3865 ASSERT(args.length() == 1);
3866
3867 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3868 return Heap::NumberFromInt32(~x);
3869}
3870
3871
3872static Object* Runtime_NumberShl(Arguments args) {
3873 NoHandleAllocation ha;
3874 ASSERT(args.length() == 2);
3875
3876 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3877 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3878 return Heap::NumberFromInt32(x << (y & 0x1f));
3879}
3880
3881
3882static Object* Runtime_NumberShr(Arguments args) {
3883 NoHandleAllocation ha;
3884 ASSERT(args.length() == 2);
3885
3886 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3887 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3888 return Heap::NumberFromUint32(x >> (y & 0x1f));
3889}
3890
3891
3892static Object* Runtime_NumberSar(Arguments args) {
3893 NoHandleAllocation ha;
3894 ASSERT(args.length() == 2);
3895
3896 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3897 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3898 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3899}
3900
3901
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003902static Object* Runtime_NumberEquals(Arguments args) {
3903 NoHandleAllocation ha;
3904 ASSERT(args.length() == 2);
3905
3906 CONVERT_DOUBLE_CHECKED(x, args[0]);
3907 CONVERT_DOUBLE_CHECKED(y, args[1]);
3908 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3909 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3910 if (x == y) return Smi::FromInt(EQUAL);
3911 Object* result;
3912 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3913 result = Smi::FromInt(EQUAL);
3914 } else {
3915 result = Smi::FromInt(NOT_EQUAL);
3916 }
3917 return result;
3918}
3919
3920
3921static Object* Runtime_StringEquals(Arguments args) {
3922 NoHandleAllocation ha;
3923 ASSERT(args.length() == 2);
3924
3925 CONVERT_CHECKED(String, x, args[0]);
3926 CONVERT_CHECKED(String, y, args[1]);
3927
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003928 bool not_equal = !x->Equals(y);
3929 // This is slightly convoluted because the value that signifies
3930 // equality is 0 and inequality is 1 so we have to negate the result
3931 // from String::Equals.
3932 ASSERT(not_equal == 0 || not_equal == 1);
3933 STATIC_CHECK(EQUAL == 0);
3934 STATIC_CHECK(NOT_EQUAL == 1);
3935 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003936}
3937
3938
3939static Object* Runtime_NumberCompare(Arguments args) {
3940 NoHandleAllocation ha;
3941 ASSERT(args.length() == 3);
3942
3943 CONVERT_DOUBLE_CHECKED(x, args[0]);
3944 CONVERT_DOUBLE_CHECKED(y, args[1]);
3945 if (isnan(x) || isnan(y)) return args[2];
3946 if (x == y) return Smi::FromInt(EQUAL);
3947 if (isless(x, y)) return Smi::FromInt(LESS);
3948 return Smi::FromInt(GREATER);
3949}
3950
3951
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003952// Compare two Smis as if they were converted to strings and then
3953// compared lexicographically.
3954static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3955 NoHandleAllocation ha;
3956 ASSERT(args.length() == 2);
3957
3958 // Arrays for the individual characters of the two Smis. Smis are
3959 // 31 bit integers and 10 decimal digits are therefore enough.
3960 static int x_elms[10];
3961 static int y_elms[10];
3962
3963 // Extract the integer values from the Smis.
3964 CONVERT_CHECKED(Smi, x, args[0]);
3965 CONVERT_CHECKED(Smi, y, args[1]);
3966 int x_value = x->value();
3967 int y_value = y->value();
3968
3969 // If the integers are equal so are the string representations.
3970 if (x_value == y_value) return Smi::FromInt(EQUAL);
3971
3972 // If one of the integers are zero the normal integer order is the
3973 // same as the lexicographic order of the string representations.
3974 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3975
ager@chromium.org32912102009-01-16 10:38:43 +00003976 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003977 // smallest because the char code of '-' is less than the char code
3978 // of any digit. Otherwise, we make both values positive.
3979 if (x_value < 0 || y_value < 0) {
3980 if (y_value >= 0) return Smi::FromInt(LESS);
3981 if (x_value >= 0) return Smi::FromInt(GREATER);
3982 x_value = -x_value;
3983 y_value = -y_value;
3984 }
3985
3986 // Convert the integers to arrays of their decimal digits.
3987 int x_index = 0;
3988 int y_index = 0;
3989 while (x_value > 0) {
3990 x_elms[x_index++] = x_value % 10;
3991 x_value /= 10;
3992 }
3993 while (y_value > 0) {
3994 y_elms[y_index++] = y_value % 10;
3995 y_value /= 10;
3996 }
3997
3998 // Loop through the arrays of decimal digits finding the first place
3999 // where they differ.
4000 while (--x_index >= 0 && --y_index >= 0) {
4001 int diff = x_elms[x_index] - y_elms[y_index];
4002 if (diff != 0) return Smi::FromInt(diff);
4003 }
4004
4005 // If one array is a suffix of the other array, the longest array is
4006 // the representation of the largest of the Smis in the
4007 // lexicographic ordering.
4008 return Smi::FromInt(x_index - y_index);
4009}
4010
4011
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004012static Object* Runtime_StringCompare(Arguments args) {
4013 NoHandleAllocation ha;
4014 ASSERT(args.length() == 2);
4015
4016 CONVERT_CHECKED(String, x, args[0]);
4017 CONVERT_CHECKED(String, y, args[1]);
4018
4019 // A few fast case tests before we flatten.
4020 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004021 if (y->length() == 0) {
4022 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004023 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004024 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004025 return Smi::FromInt(LESS);
4026 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004027
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004028 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004029 if (d < 0) return Smi::FromInt(LESS);
4030 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004032 x->TryFlattenIfNotFlat();
4033 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004034
4035 static StringInputBuffer bufx;
4036 static StringInputBuffer bufy;
4037 bufx.Reset(x);
4038 bufy.Reset(y);
4039 while (bufx.has_more() && bufy.has_more()) {
4040 int d = bufx.GetNext() - bufy.GetNext();
4041 if (d < 0) return Smi::FromInt(LESS);
4042 else if (d > 0) return Smi::FromInt(GREATER);
4043 }
4044
4045 // x is (non-trivial) prefix of y:
4046 if (bufy.has_more()) return Smi::FromInt(LESS);
4047 // y is prefix of x:
4048 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4049}
4050
4051
4052static Object* Runtime_Math_abs(Arguments args) {
4053 NoHandleAllocation ha;
4054 ASSERT(args.length() == 1);
4055
4056 CONVERT_DOUBLE_CHECKED(x, args[0]);
4057 return Heap::AllocateHeapNumber(fabs(x));
4058}
4059
4060
4061static Object* Runtime_Math_acos(Arguments args) {
4062 NoHandleAllocation ha;
4063 ASSERT(args.length() == 1);
4064
4065 CONVERT_DOUBLE_CHECKED(x, args[0]);
4066 return Heap::AllocateHeapNumber(acos(x));
4067}
4068
4069
4070static Object* Runtime_Math_asin(Arguments args) {
4071 NoHandleAllocation ha;
4072 ASSERT(args.length() == 1);
4073
4074 CONVERT_DOUBLE_CHECKED(x, args[0]);
4075 return Heap::AllocateHeapNumber(asin(x));
4076}
4077
4078
4079static Object* Runtime_Math_atan(Arguments args) {
4080 NoHandleAllocation ha;
4081 ASSERT(args.length() == 1);
4082
4083 CONVERT_DOUBLE_CHECKED(x, args[0]);
4084 return Heap::AllocateHeapNumber(atan(x));
4085}
4086
4087
4088static Object* Runtime_Math_atan2(Arguments args) {
4089 NoHandleAllocation ha;
4090 ASSERT(args.length() == 2);
4091
4092 CONVERT_DOUBLE_CHECKED(x, args[0]);
4093 CONVERT_DOUBLE_CHECKED(y, args[1]);
4094 double result;
4095 if (isinf(x) && isinf(y)) {
4096 // Make sure that the result in case of two infinite arguments
4097 // is a multiple of Pi / 4. The sign of the result is determined
4098 // by the first argument (x) and the sign of the second argument
4099 // determines the multiplier: one or three.
4100 static double kPiDividedBy4 = 0.78539816339744830962;
4101 int multiplier = (x < 0) ? -1 : 1;
4102 if (y < 0) multiplier *= 3;
4103 result = multiplier * kPiDividedBy4;
4104 } else {
4105 result = atan2(x, y);
4106 }
4107 return Heap::AllocateHeapNumber(result);
4108}
4109
4110
4111static Object* Runtime_Math_ceil(Arguments args) {
4112 NoHandleAllocation ha;
4113 ASSERT(args.length() == 1);
4114
4115 CONVERT_DOUBLE_CHECKED(x, args[0]);
4116 return Heap::NumberFromDouble(ceiling(x));
4117}
4118
4119
4120static Object* Runtime_Math_cos(Arguments args) {
4121 NoHandleAllocation ha;
4122 ASSERT(args.length() == 1);
4123
4124 CONVERT_DOUBLE_CHECKED(x, args[0]);
4125 return Heap::AllocateHeapNumber(cos(x));
4126}
4127
4128
4129static Object* Runtime_Math_exp(Arguments args) {
4130 NoHandleAllocation ha;
4131 ASSERT(args.length() == 1);
4132
4133 CONVERT_DOUBLE_CHECKED(x, args[0]);
4134 return Heap::AllocateHeapNumber(exp(x));
4135}
4136
4137
4138static Object* Runtime_Math_floor(Arguments args) {
4139 NoHandleAllocation ha;
4140 ASSERT(args.length() == 1);
4141
4142 CONVERT_DOUBLE_CHECKED(x, args[0]);
4143 return Heap::NumberFromDouble(floor(x));
4144}
4145
4146
4147static Object* Runtime_Math_log(Arguments args) {
4148 NoHandleAllocation ha;
4149 ASSERT(args.length() == 1);
4150
4151 CONVERT_DOUBLE_CHECKED(x, args[0]);
4152 return Heap::AllocateHeapNumber(log(x));
4153}
4154
4155
4156static Object* Runtime_Math_pow(Arguments args) {
4157 NoHandleAllocation ha;
4158 ASSERT(args.length() == 2);
4159
4160 CONVERT_DOUBLE_CHECKED(x, args[0]);
4161 CONVERT_DOUBLE_CHECKED(y, args[1]);
4162 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4163 return Heap::nan_value();
4164 } else if (y == 0) {
4165 return Smi::FromInt(1);
4166 } else {
4167 return Heap::AllocateHeapNumber(pow(x, y));
4168 }
4169}
4170
4171// Returns a number value with positive sign, greater than or equal to
4172// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00004173static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004174 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004175 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004176
4177 // To get much better precision, we combine the results of two
4178 // invocations of random(). The result is computed by normalizing a
4179 // double in the range [0, RAND_MAX + 1) obtained by adding the
4180 // high-order bits in the range [0, RAND_MAX] with the low-order
4181 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004182 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004183 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004184 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004185 ASSERT(result >= 0 && result < 1);
4186 return Heap::AllocateHeapNumber(result);
4187}
4188
4189
4190static Object* Runtime_Math_round(Arguments args) {
4191 NoHandleAllocation ha;
4192 ASSERT(args.length() == 1);
4193
4194 CONVERT_DOUBLE_CHECKED(x, args[0]);
4195 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4196 return Heap::NumberFromDouble(floor(x + 0.5));
4197}
4198
4199
4200static Object* Runtime_Math_sin(Arguments args) {
4201 NoHandleAllocation ha;
4202 ASSERT(args.length() == 1);
4203
4204 CONVERT_DOUBLE_CHECKED(x, args[0]);
4205 return Heap::AllocateHeapNumber(sin(x));
4206}
4207
4208
4209static Object* Runtime_Math_sqrt(Arguments args) {
4210 NoHandleAllocation ha;
4211 ASSERT(args.length() == 1);
4212
4213 CONVERT_DOUBLE_CHECKED(x, args[0]);
4214 return Heap::AllocateHeapNumber(sqrt(x));
4215}
4216
4217
4218static Object* Runtime_Math_tan(Arguments args) {
4219 NoHandleAllocation ha;
4220 ASSERT(args.length() == 1);
4221
4222 CONVERT_DOUBLE_CHECKED(x, args[0]);
4223 return Heap::AllocateHeapNumber(tan(x));
4224}
4225
4226
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004227// The NewArguments function is only used when constructing the
4228// arguments array when calling non-functions from JavaScript in
4229// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004230static Object* Runtime_NewArguments(Arguments args) {
4231 NoHandleAllocation ha;
4232 ASSERT(args.length() == 1);
4233
4234 // ECMA-262, 3rd., 10.1.8, p.39
4235 CONVERT_CHECKED(JSFunction, callee, args[0]);
4236
4237 // Compute the frame holding the arguments.
4238 JavaScriptFrameIterator it;
4239 it.AdvanceToArgumentsFrame();
4240 JavaScriptFrame* frame = it.frame();
4241
4242 const int length = frame->GetProvidedParametersCount();
4243 Object* result = Heap::AllocateArgumentsObject(callee, length);
4244 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004245 if (length > 0) {
4246 Object* obj = Heap::AllocateFixedArray(length);
4247 if (obj->IsFailure()) return obj;
4248 FixedArray* array = FixedArray::cast(obj);
4249 ASSERT(array->length() == length);
4250 WriteBarrierMode mode = array->GetWriteBarrierMode();
4251 for (int i = 0; i < length; i++) {
4252 array->set(i, frame->GetParameter(i), mode);
4253 }
4254 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004255 }
4256 return result;
4257}
4258
4259
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004260static Object* Runtime_NewArgumentsFast(Arguments args) {
4261 NoHandleAllocation ha;
4262 ASSERT(args.length() == 3);
4263
4264 JSFunction* callee = JSFunction::cast(args[0]);
4265 Object** parameters = reinterpret_cast<Object**>(args[1]);
4266 const int length = Smi::cast(args[2])->value();
4267
4268 Object* result = Heap::AllocateArgumentsObject(callee, length);
4269 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004270 ASSERT(Heap::InNewSpace(result));
4271
4272 // Allocate the elements if needed.
4273 if (length > 0) {
4274 // Allocate the fixed array.
4275 Object* obj = Heap::AllocateRawFixedArray(length);
4276 if (obj->IsFailure()) return obj;
4277 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4278 FixedArray* array = FixedArray::cast(obj);
4279 array->set_length(length);
4280 WriteBarrierMode mode = array->GetWriteBarrierMode();
4281 for (int i = 0; i < length; i++) {
4282 array->set(i, *--parameters, mode);
4283 }
4284 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4285 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004286 }
4287 return result;
4288}
4289
4290
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004291static Object* Runtime_NewClosure(Arguments args) {
4292 HandleScope scope;
4293 ASSERT(args.length() == 2);
4294 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4295 CONVERT_ARG_CHECKED(Context, context, 1);
4296
4297 Handle<JSFunction> result =
4298 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4299 return *result;
4300}
4301
4302
4303static Object* Runtime_NewObject(Arguments args) {
4304 NoHandleAllocation ha;
4305 ASSERT(args.length() == 1);
4306
4307 Object* constructor = args[0];
4308 if (constructor->IsJSFunction()) {
4309 JSFunction* function = JSFunction::cast(constructor);
4310
ager@chromium.org32912102009-01-16 10:38:43 +00004311 // Handle stepping into constructors if step into is active.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004312#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004313 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004314 HandleScope scope;
4315 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004316 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004317#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004318
4319 if (function->has_initial_map() &&
4320 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
4321 // The 'Function' function ignores the receiver object when
4322 // called using 'new' and creates a new JSFunction object that
4323 // is returned. The receiver object is only used for error
4324 // reporting if an error occurs when constructing the new
4325 // JSFunction. AllocateJSObject should not be used to allocate
4326 // JSFunctions since it does not properly initialize the shared
4327 // part of the function. Since the receiver is ignored anyway,
4328 // we use the global object as the receiver instead of a new
4329 // JSFunction object. This way, errors are reported the same
4330 // way whether or not 'Function' is called using 'new'.
4331 return Top::context()->global();
4332 }
4333 return Heap::AllocateJSObject(function);
4334 }
4335
4336 HandleScope scope;
4337 Handle<Object> cons(constructor);
4338 // The constructor is not a function; throw a type error.
4339 Handle<Object> type_error =
4340 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
4341 return Top::Throw(*type_error);
4342}
4343
4344
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004345static Object* Runtime_LazyCompile(Arguments args) {
4346 HandleScope scope;
4347 ASSERT(args.length() == 1);
4348
4349 Handle<JSFunction> function = args.at<JSFunction>(0);
4350#ifdef DEBUG
4351 if (FLAG_trace_lazy) {
4352 PrintF("[lazy: ");
4353 function->shared()->name()->Print();
4354 PrintF("]\n");
4355 }
4356#endif
4357
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004358 // Compile the target function. Here we compile using CompileLazyInLoop in
4359 // order to get the optimized version. This helps code like delta-blue
4360 // that calls performance-critical routines through constructors. A
4361 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4362 // direct call. Since the in-loop tracking takes place through CallICs
4363 // this means that things called through constructors are never known to
4364 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004365 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004366 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004367 return Failure::Exception();
4368 }
4369
4370 return function->code();
4371}
4372
4373
4374static Object* Runtime_GetCalledFunction(Arguments args) {
4375 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004376 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004377 StackFrameIterator it;
4378 // Get past the JS-to-C exit frame.
4379 ASSERT(it.frame()->is_exit());
4380 it.Advance();
4381 // Get past the CALL_NON_FUNCTION activation frame.
4382 ASSERT(it.frame()->is_java_script());
4383 it.Advance();
4384 // Argument adaptor frames do not copy the function; we have to skip
4385 // past them to get to the real calling frame.
4386 if (it.frame()->is_arguments_adaptor()) it.Advance();
4387 // Get the function from the top of the expression stack of the
4388 // calling frame.
4389 StandardFrame* frame = StandardFrame::cast(it.frame());
4390 int index = frame->ComputeExpressionsCount() - 1;
4391 Object* result = frame->GetExpression(index);
4392 return result;
4393}
4394
4395
4396static Object* Runtime_GetFunctionDelegate(Arguments args) {
4397 HandleScope scope;
4398 ASSERT(args.length() == 1);
4399 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4400 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4401}
4402
4403
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004404static Object* Runtime_GetConstructorDelegate(Arguments args) {
4405 HandleScope scope;
4406 ASSERT(args.length() == 1);
4407 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4408 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4409}
4410
4411
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004412static Object* Runtime_NewContext(Arguments args) {
4413 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004414 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004415
kasper.lund7276f142008-07-30 08:49:36 +00004416 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004417 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4418 Object* result = Heap::AllocateFunctionContext(length, function);
4419 if (result->IsFailure()) return result;
4420
4421 Top::set_context(Context::cast(result));
4422
kasper.lund7276f142008-07-30 08:49:36 +00004423 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004424}
4425
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004426static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004427 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004428 Object* js_object = object;
4429 if (!js_object->IsJSObject()) {
4430 js_object = js_object->ToObject();
4431 if (js_object->IsFailure()) {
4432 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004433 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004434 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004435 Handle<Object> result =
4436 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4437 return Top::Throw(*result);
4438 }
4439 }
4440
4441 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004442 Heap::AllocateWithContext(Top::context(),
4443 JSObject::cast(js_object),
4444 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004445 if (result->IsFailure()) return result;
4446
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004447 Context* context = Context::cast(result);
4448 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004449
kasper.lund7276f142008-07-30 08:49:36 +00004450 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004451}
4452
4453
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004454static Object* Runtime_PushContext(Arguments args) {
4455 NoHandleAllocation ha;
4456 ASSERT(args.length() == 1);
4457 return PushContextHelper(args[0], false);
4458}
4459
4460
4461static Object* Runtime_PushCatchContext(Arguments args) {
4462 NoHandleAllocation ha;
4463 ASSERT(args.length() == 1);
4464 return PushContextHelper(args[0], true);
4465}
4466
4467
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004468static Object* Runtime_LookupContext(Arguments args) {
4469 HandleScope scope;
4470 ASSERT(args.length() == 2);
4471
4472 CONVERT_ARG_CHECKED(Context, context, 0);
4473 CONVERT_ARG_CHECKED(String, name, 1);
4474
4475 int index;
4476 PropertyAttributes attributes;
4477 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004478 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004479 context->Lookup(name, flags, &index, &attributes);
4480
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004481 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004482 ASSERT(holder->IsJSObject());
4483 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004484 }
4485
4486 // No intermediate context found. Use global object by default.
4487 return Top::context()->global();
4488}
4489
4490
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004491// A mechanism to return pairs of Object*'s. This is somewhat
4492// compiler-dependent as it assumes that a 64-bit value (a long long)
4493// is returned via two registers (edx:eax on ia32). Both the ia32 and
4494// arm platform support this; it is mostly an issue of "coaxing" the
4495// compiler to do the right thing.
4496//
4497// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004498// TODO(x64): Definitely!
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004499typedef uint64_t ObjectPair;
4500static inline ObjectPair MakePair(Object* x, Object* y) {
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004501#if V8_HOST_ARCH_64_BIT
4502 UNIMPLEMENTED();
4503 return 0;
4504#else
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004505 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004506 (reinterpret_cast<ObjectPair>(y) << 32);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00004507#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004508}
4509
4510
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004511static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004512 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4513 USE(attributes);
4514 return x->IsTheHole() ? Heap::undefined_value() : x;
4515}
4516
4517
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004518static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4519 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004520 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004521 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004522 JSFunction* context_extension_function =
4523 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004524 // If the holder isn't a context extension object, we just return it
4525 // as the receiver. This allows arguments objects to be used as
4526 // receivers, but only if they are put in the context scope chain
4527 // explicitly via a with-statement.
4528 Object* constructor = holder->map()->constructor();
4529 if (constructor != context_extension_function) return holder;
4530 // Fall back to using the global object as the receiver if the
4531 // property turns out to be a local variable allocated in a context
4532 // extension object - introduced via eval.
4533 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004534}
4535
4536
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004537static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538 HandleScope scope;
4539 ASSERT(args.length() == 2);
4540
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004541 if (!args[0]->IsContext() || !args[1]->IsString()) {
4542 return MakePair(IllegalOperation(), NULL);
4543 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004544 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004545 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004546
4547 int index;
4548 PropertyAttributes attributes;
4549 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004550 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004551 context->Lookup(name, flags, &index, &attributes);
4552
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004553 // If the index is non-negative, the slot has been found in a local
4554 // variable or a parameter. Read it from the context object or the
4555 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004556 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004557 // If the "property" we were looking for is a local variable or an
4558 // argument in a context, the receiver is the global object; see
4559 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4560 JSObject* receiver = Top::context()->global()->global_receiver();
4561 Object* value = (holder->IsContext())
4562 ? Context::cast(*holder)->get(index)
4563 : JSObject::cast(*holder)->GetElement(index);
4564 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004565 }
4566
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004567 // If the holder is found, we read the property from it.
4568 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004569 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004570 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004571 JSObject* receiver;
4572 if (object->IsGlobalObject()) {
4573 receiver = GlobalObject::cast(object)->global_receiver();
4574 } else if (context->is_exception_holder(*holder)) {
4575 receiver = Top::context()->global()->global_receiver();
4576 } else {
4577 receiver = ComputeReceiverForNonGlobal(object);
4578 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004579 // No need to unhole the value here. This is taken care of by the
4580 // GetProperty function.
4581 Object* value = object->GetProperty(*name);
4582 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004583 }
4584
4585 if (throw_error) {
4586 // The property doesn't exist - throw exception.
4587 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004588 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004589 return MakePair(Top::Throw(*reference_error), NULL);
4590 } else {
4591 // The property doesn't exist - return undefined
4592 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4593 }
4594}
4595
4596
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004597static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004598 return LoadContextSlotHelper(args, true);
4599}
4600
4601
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004602static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004603 return LoadContextSlotHelper(args, false);
4604}
4605
4606
4607static Object* Runtime_StoreContextSlot(Arguments args) {
4608 HandleScope scope;
4609 ASSERT(args.length() == 3);
4610
4611 Handle<Object> value(args[0]);
4612 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004613 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004614
4615 int index;
4616 PropertyAttributes attributes;
4617 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004618 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004619 context->Lookup(name, flags, &index, &attributes);
4620
4621 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004622 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004623 // Ignore if read_only variable.
4624 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004625 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004626 }
4627 } else {
4628 ASSERT((attributes & READ_ONLY) == 0);
4629 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004630 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004631 USE(result);
4632 ASSERT(!result->IsFailure());
4633 }
4634 return *value;
4635 }
4636
4637 // Slow case: The property is not in a FixedArray context.
4638 // It is either in an JSObject extension context or it was not found.
4639 Handle<JSObject> context_ext;
4640
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004641 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004642 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004643 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004644 } else {
4645 // The property was not found. It needs to be stored in the global context.
4646 ASSERT(attributes == ABSENT);
4647 attributes = NONE;
4648 context_ext = Handle<JSObject>(Top::context()->global());
4649 }
4650
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004651 // Set the property, but ignore if read_only variable on the context
4652 // extension object itself.
4653 if ((attributes & READ_ONLY) == 0 ||
4654 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004655 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4656 if (set.is_null()) {
4657 // Failure::Exception is converted to a null handle in the
4658 // handle-based methods such as SetProperty. We therefore need
4659 // to convert null handles back to exceptions.
4660 ASSERT(Top::has_pending_exception());
4661 return Failure::Exception();
4662 }
4663 }
4664 return *value;
4665}
4666
4667
4668static Object* Runtime_Throw(Arguments args) {
4669 HandleScope scope;
4670 ASSERT(args.length() == 1);
4671
4672 return Top::Throw(args[0]);
4673}
4674
4675
4676static Object* Runtime_ReThrow(Arguments args) {
4677 HandleScope scope;
4678 ASSERT(args.length() == 1);
4679
4680 return Top::ReThrow(args[0]);
4681}
4682
4683
4684static Object* Runtime_ThrowReferenceError(Arguments args) {
4685 HandleScope scope;
4686 ASSERT(args.length() == 1);
4687
4688 Handle<Object> name(args[0]);
4689 Handle<Object> reference_error =
4690 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4691 return Top::Throw(*reference_error);
4692}
4693
4694
4695static Object* Runtime_StackOverflow(Arguments args) {
4696 NoHandleAllocation na;
4697 return Top::StackOverflow();
4698}
4699
4700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004701static Object* Runtime_StackGuard(Arguments args) {
4702 ASSERT(args.length() == 1);
4703
4704 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004705 if (StackGuard::IsStackOverflow()) {
4706 return Runtime_StackOverflow(args);
4707 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004708
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004709 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004710}
4711
4712
4713// NOTE: These PrintXXX functions are defined for all builds (not just
4714// DEBUG builds) because we may want to be able to trace function
4715// calls in all modes.
4716static void PrintString(String* str) {
4717 // not uncommon to have empty strings
4718 if (str->length() > 0) {
4719 SmartPointer<char> s =
4720 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4721 PrintF("%s", *s);
4722 }
4723}
4724
4725
4726static void PrintObject(Object* obj) {
4727 if (obj->IsSmi()) {
4728 PrintF("%d", Smi::cast(obj)->value());
4729 } else if (obj->IsString() || obj->IsSymbol()) {
4730 PrintString(String::cast(obj));
4731 } else if (obj->IsNumber()) {
4732 PrintF("%g", obj->Number());
4733 } else if (obj->IsFailure()) {
4734 PrintF("<failure>");
4735 } else if (obj->IsUndefined()) {
4736 PrintF("<undefined>");
4737 } else if (obj->IsNull()) {
4738 PrintF("<null>");
4739 } else if (obj->IsTrue()) {
4740 PrintF("<true>");
4741 } else if (obj->IsFalse()) {
4742 PrintF("<false>");
4743 } else {
4744 PrintF("%p", obj);
4745 }
4746}
4747
4748
4749static int StackSize() {
4750 int n = 0;
4751 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4752 return n;
4753}
4754
4755
4756static void PrintTransition(Object* result) {
4757 // indentation
4758 { const int nmax = 80;
4759 int n = StackSize();
4760 if (n <= nmax)
4761 PrintF("%4d:%*s", n, n, "");
4762 else
4763 PrintF("%4d:%*s", n, nmax, "...");
4764 }
4765
4766 if (result == NULL) {
4767 // constructor calls
4768 JavaScriptFrameIterator it;
4769 JavaScriptFrame* frame = it.frame();
4770 if (frame->IsConstructor()) PrintF("new ");
4771 // function name
4772 Object* fun = frame->function();
4773 if (fun->IsJSFunction()) {
4774 PrintObject(JSFunction::cast(fun)->shared()->name());
4775 } else {
4776 PrintObject(fun);
4777 }
4778 // function arguments
4779 // (we are intentionally only printing the actually
4780 // supplied parameters, not all parameters required)
4781 PrintF("(this=");
4782 PrintObject(frame->receiver());
4783 const int length = frame->GetProvidedParametersCount();
4784 for (int i = 0; i < length; i++) {
4785 PrintF(", ");
4786 PrintObject(frame->GetParameter(i));
4787 }
4788 PrintF(") {\n");
4789
4790 } else {
4791 // function result
4792 PrintF("} -> ");
4793 PrintObject(result);
4794 PrintF("\n");
4795 }
4796}
4797
4798
4799static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004800 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004801 NoHandleAllocation ha;
4802 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004803 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004804}
4805
4806
4807static Object* Runtime_TraceExit(Arguments args) {
4808 NoHandleAllocation ha;
4809 PrintTransition(args[0]);
4810 return args[0]; // return TOS
4811}
4812
4813
4814static Object* Runtime_DebugPrint(Arguments args) {
4815 NoHandleAllocation ha;
4816 ASSERT(args.length() == 1);
4817
4818#ifdef DEBUG
4819 if (args[0]->IsString()) {
4820 // If we have a string, assume it's a code "marker"
4821 // and print some interesting cpu debugging info.
4822 JavaScriptFrameIterator it;
4823 JavaScriptFrame* frame = it.frame();
4824 PrintF("fp = %p, sp = %p, pp = %p: ",
4825 frame->fp(), frame->sp(), frame->pp());
4826 } else {
4827 PrintF("DebugPrint: ");
4828 }
4829 args[0]->Print();
4830#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004831 // ShortPrint is available in release mode. Print is not.
4832 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004833#endif
4834 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004835 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004836
4837 return args[0]; // return TOS
4838}
4839
4840
4841static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004842 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004843 NoHandleAllocation ha;
4844 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004845 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004846}
4847
4848
mads.s.ager31e71382008-08-13 09:32:07 +00004849static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004850 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004851 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852
4853 // According to ECMA-262, section 15.9.1, page 117, the precision of
4854 // the number in a Date object representing a particular instant in
4855 // time is milliseconds. Therefore, we floor the result of getting
4856 // the OS time.
4857 double millis = floor(OS::TimeCurrentMillis());
4858 return Heap::NumberFromDouble(millis);
4859}
4860
4861
4862static Object* Runtime_DateParseString(Arguments args) {
4863 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004864 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004865
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004866 CONVERT_ARG_CHECKED(String, str, 0);
4867 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004868
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004869 CONVERT_ARG_CHECKED(JSArray, output, 1);
4870 RUNTIME_ASSERT(output->HasFastElements());
4871
4872 AssertNoAllocation no_allocation;
4873
4874 FixedArray* output_array = output->elements();
4875 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4876 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004877 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004878 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004879 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004880 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004881 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4882 }
4883
4884 if (result) {
4885 return *output;
4886 } else {
4887 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004888 }
4889}
4890
4891
4892static Object* Runtime_DateLocalTimezone(Arguments args) {
4893 NoHandleAllocation ha;
4894 ASSERT(args.length() == 1);
4895
4896 CONVERT_DOUBLE_CHECKED(x, args[0]);
4897 char* zone = OS::LocalTimezone(x);
4898 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4899}
4900
4901
4902static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4903 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004904 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004905
4906 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4907}
4908
4909
4910static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4911 NoHandleAllocation ha;
4912 ASSERT(args.length() == 1);
4913
4914 CONVERT_DOUBLE_CHECKED(x, args[0]);
4915 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4916}
4917
4918
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004919static Object* Runtime_NumberIsFinite(Arguments args) {
4920 NoHandleAllocation ha;
4921 ASSERT(args.length() == 1);
4922
4923 CONVERT_DOUBLE_CHECKED(value, args[0]);
4924 Object* result;
4925 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4926 result = Heap::false_value();
4927 } else {
4928 result = Heap::true_value();
4929 }
4930 return result;
4931}
4932
4933
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004934static Object* Runtime_GlobalReceiver(Arguments args) {
4935 ASSERT(args.length() == 1);
4936 Object* global = args[0];
4937 if (!global->IsJSGlobalObject()) return Heap::null_value();
4938 return JSGlobalObject::cast(global)->global_receiver();
4939}
4940
4941
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004942static Object* Runtime_CompileString(Arguments args) {
4943 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004944 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004945 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004946 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004947
ager@chromium.org381abbb2009-02-25 13:23:22 +00004948 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004949 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004950 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4951 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004952 true,
4953 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004954 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004955 Handle<JSFunction> fun =
4956 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4957 return *fun;
4958}
4959
4960
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004961static Handle<JSFunction> GetBuiltinFunction(String* name) {
4962 LookupResult result;
4963 Top::global_context()->builtins()->LocalLookup(name, &result);
4964 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4965}
4966
4967
4968static Object* CompileDirectEval(Handle<String> source) {
4969 // Compute the eval context.
4970 HandleScope scope;
4971 StackFrameLocator locator;
4972 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4973 Handle<Context> context(Context::cast(frame->context()));
4974 bool is_global = context->IsGlobalContext();
4975
ager@chromium.org381abbb2009-02-25 13:23:22 +00004976 // Compile source string in the current context.
4977 Handle<JSFunction> boilerplate =
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004978 Compiler::CompileEval(source, context, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004979 if (boilerplate.is_null()) return Failure::Exception();
4980 Handle<JSFunction> fun =
4981 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4982 return *fun;
4983}
4984
4985
4986static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4987 ASSERT(args.length() == 2);
4988
4989 HandleScope scope;
4990
4991 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4992
4993 Handle<Object> receiver;
4994
4995 // Find where the 'eval' symbol is bound. It is unaliased only if
4996 // it is bound in the global context.
4997 StackFrameLocator locator;
4998 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4999 Handle<Context> context(Context::cast(frame->context()));
5000 int index;
5001 PropertyAttributes attributes;
5002 while (!context.is_null()) {
5003 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5004 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005005 // Stop search when eval is found or when the global context is
5006 // reached.
5007 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005008 if (context->is_function_context()) {
5009 context = Handle<Context>(Context::cast(context->closure()->context()));
5010 } else {
5011 context = Handle<Context>(context->previous());
5012 }
5013 }
5014
iposva@chromium.org245aa852009-02-10 00:49:54 +00005015 // If eval could not be resolved, it has been deleted and we need to
5016 // throw a reference error.
5017 if (attributes == ABSENT) {
5018 Handle<Object> name = Factory::eval_symbol();
5019 Handle<Object> reference_error =
5020 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5021 return Top::Throw(*reference_error);
5022 }
5023
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005024 if (context->IsGlobalContext()) {
5025 // 'eval' is bound in the global context, but it may have been overwritten.
5026 // Compare it to the builtin 'GlobalEval' function to make sure.
5027 Handle<JSFunction> global_eval =
5028 GetBuiltinFunction(Heap::global_eval_symbol());
5029 if (global_eval.is_identical_to(callee)) {
5030 // A direct eval call.
5031 if (args[1]->IsString()) {
5032 CONVERT_ARG_CHECKED(String, source, 1);
5033 // A normal eval call on a string. Compile it and return the
5034 // compiled function bound in the local context.
5035 Object* compiled_source = CompileDirectEval(source);
5036 if (compiled_source->IsFailure()) return compiled_source;
5037 receiver = Handle<Object>(frame->receiver());
5038 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5039 } else {
5040 // An eval call that is not called on a string. Global eval
5041 // deals better with this.
5042 receiver = Handle<Object>(Top::global_context()->global());
5043 }
5044 } else {
5045 // 'eval' is overwritten. Just call the function with the given arguments.
5046 receiver = Handle<Object>(Top::global_context()->global());
5047 }
5048 } else {
5049 // 'eval' is not bound in the global context. Just call the function
5050 // with the given arguments. This is not necessarily the global eval.
5051 if (receiver->IsContext()) {
5052 context = Handle<Context>::cast(receiver);
5053 receiver = Handle<Object>(context->get(index));
5054 }
5055 }
5056
5057 Handle<FixedArray> call = Factory::NewFixedArray(2);
5058 call->set(0, *callee);
5059 call->set(1, *receiver);
5060 return *call;
5061}
5062
5063
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005064static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5065 // This utility adjusts the property attributes for newly created Function
5066 // object ("new Function(...)") by changing the map.
5067 // All it does is changing the prototype property to enumerable
5068 // as specified in ECMA262, 15.3.5.2.
5069 HandleScope scope;
5070 ASSERT(args.length() == 1);
5071 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5072 ASSERT(func->map()->instance_type() ==
5073 Top::function_instance_map()->instance_type());
5074 ASSERT(func->map()->instance_size() ==
5075 Top::function_instance_map()->instance_size());
5076 func->set_map(*Top::function_instance_map());
5077 return *func;
5078}
5079
5080
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005081// Push an array unto an array of arrays if it is not already in the
5082// array. Returns true if the element was pushed on the stack and
5083// false otherwise.
5084static Object* Runtime_PushIfAbsent(Arguments args) {
5085 ASSERT(args.length() == 2);
5086 CONVERT_CHECKED(JSArray, array, args[0]);
5087 CONVERT_CHECKED(JSArray, element, args[1]);
5088 RUNTIME_ASSERT(array->HasFastElements());
5089 int length = Smi::cast(array->length())->value();
5090 FixedArray* elements = FixedArray::cast(array->elements());
5091 for (int i = 0; i < length; i++) {
5092 if (elements->get(i) == element) return Heap::false_value();
5093 }
5094 Object* obj = array->SetFastElement(length, element);
5095 if (obj->IsFailure()) return obj;
5096 return Heap::true_value();
5097}
5098
5099
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005100/**
5101 * A simple visitor visits every element of Array's.
5102 * The backend storage can be a fixed array for fast elements case,
5103 * or a dictionary for sparse array. Since Dictionary is a subtype
5104 * of FixedArray, the class can be used by both fast and slow cases.
5105 * The second parameter of the constructor, fast_elements, specifies
5106 * whether the storage is a FixedArray or Dictionary.
5107 *
5108 * An index limit is used to deal with the situation that a result array
5109 * length overflows 32-bit non-negative integer.
5110 */
5111class ArrayConcatVisitor {
5112 public:
5113 ArrayConcatVisitor(Handle<FixedArray> storage,
5114 uint32_t index_limit,
5115 bool fast_elements) :
5116 storage_(storage), index_limit_(index_limit),
5117 fast_elements_(fast_elements), index_offset_(0) { }
5118
5119 void visit(uint32_t i, Handle<Object> elm) {
5120 uint32_t index = i + index_offset_;
5121 if (index >= index_limit_) return;
5122
5123 if (fast_elements_) {
5124 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5125 storage_->set(index, *elm);
5126
5127 } else {
5128 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5129 Handle<Dictionary> result =
5130 Factory::DictionaryAtNumberPut(dict, index, elm);
5131 if (!result.is_identical_to(dict))
5132 storage_ = result;
5133 }
5134 }
5135
5136 void increase_index_offset(uint32_t delta) {
5137 index_offset_ += delta;
5138 }
5139
5140 private:
5141 Handle<FixedArray> storage_;
5142 uint32_t index_limit_;
5143 bool fast_elements_;
5144 uint32_t index_offset_;
5145};
5146
5147
5148/**
5149 * A helper function that visits elements of a JSObject. Only elements
5150 * whose index between 0 and range (exclusive) are visited.
5151 *
5152 * If the third parameter, visitor, is not NULL, the visitor is called
5153 * with parameters, 'visitor_index_offset + element index' and the element.
5154 *
5155 * It returns the number of visisted elements.
5156 */
5157static uint32_t IterateElements(Handle<JSObject> receiver,
5158 uint32_t range,
5159 ArrayConcatVisitor* visitor) {
5160 uint32_t num_of_elements = 0;
5161
5162 if (receiver->HasFastElements()) {
5163 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5164 uint32_t len = elements->length();
5165 if (range < len) len = range;
5166
5167 for (uint32_t j = 0; j < len; j++) {
5168 Handle<Object> e(elements->get(j));
5169 if (!e->IsTheHole()) {
5170 num_of_elements++;
5171 if (visitor)
5172 visitor->visit(j, e);
5173 }
5174 }
5175
5176 } else {
5177 Handle<Dictionary> dict(receiver->element_dictionary());
5178 uint32_t capacity = dict->Capacity();
5179 for (uint32_t j = 0; j < capacity; j++) {
5180 Handle<Object> k(dict->KeyAt(j));
5181 if (dict->IsKey(*k)) {
5182 ASSERT(k->IsNumber());
5183 uint32_t index = static_cast<uint32_t>(k->Number());
5184 if (index < range) {
5185 num_of_elements++;
5186 if (visitor) {
5187 visitor->visit(index,
5188 Handle<Object>(dict->ValueAt(j)));
5189 }
5190 }
5191 }
5192 }
5193 }
5194
5195 return num_of_elements;
5196}
5197
5198
5199/**
5200 * A helper function that visits elements of an Array object, and elements
5201 * on its prototypes.
5202 *
5203 * Elements on prototypes are visited first, and only elements whose indices
5204 * less than Array length are visited.
5205 *
5206 * If a ArrayConcatVisitor object is given, the visitor is called with
5207 * parameters, element's index + visitor_index_offset and the element.
5208 */
5209static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5210 ArrayConcatVisitor* visitor) {
5211 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5212 Handle<Object> obj = array;
5213
5214 static const int kEstimatedPrototypes = 3;
5215 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5216
5217 // Visit prototype first. If an element on the prototype is shadowed by
5218 // the inheritor using the same index, the ArrayConcatVisitor visits
5219 // the prototype element before the shadowing element.
5220 // The visitor can simply overwrite the old value by new value using
5221 // the same index. This follows Array::concat semantics.
5222 while (!obj->IsNull()) {
5223 objects.Add(Handle<JSObject>::cast(obj));
5224 obj = Handle<Object>(obj->GetPrototype());
5225 }
5226
5227 uint32_t nof_elements = 0;
5228 for (int i = objects.length() - 1; i >= 0; i--) {
5229 Handle<JSObject> obj = objects[i];
5230 nof_elements +=
5231 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5232 }
5233
5234 return nof_elements;
5235}
5236
5237
5238/**
5239 * A helper function of Runtime_ArrayConcat.
5240 *
5241 * The first argument is an Array of arrays and objects. It is the
5242 * same as the arguments array of Array::concat JS function.
5243 *
5244 * If an argument is an Array object, the function visits array
5245 * elements. If an argument is not an Array object, the function
5246 * visits the object as if it is an one-element array.
5247 *
5248 * If the result array index overflows 32-bit integer, the rounded
5249 * non-negative number is used as new length. For example, if one
5250 * array length is 2^32 - 1, second array length is 1, the
5251 * concatenated array length is 0.
5252 */
5253static uint32_t IterateArguments(Handle<JSArray> arguments,
5254 ArrayConcatVisitor* visitor) {
5255 uint32_t visited_elements = 0;
5256 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5257
5258 for (uint32_t i = 0; i < num_of_args; i++) {
5259 Handle<Object> obj(arguments->GetElement(i));
5260 if (obj->IsJSArray()) {
5261 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5262 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5263 uint32_t nof_elements =
5264 IterateArrayAndPrototypeElements(array, visitor);
5265 // Total elements of array and its prototype chain can be more than
5266 // the array length, but ArrayConcat can only concatenate at most
5267 // the array length number of elements.
5268 visited_elements += (nof_elements > len) ? len : nof_elements;
5269 if (visitor) visitor->increase_index_offset(len);
5270
5271 } else {
5272 if (visitor) {
5273 visitor->visit(0, obj);
5274 visitor->increase_index_offset(1);
5275 }
5276 visited_elements++;
5277 }
5278 }
5279 return visited_elements;
5280}
5281
5282
5283/**
5284 * Array::concat implementation.
5285 * See ECMAScript 262, 15.4.4.4.
5286 */
5287static Object* Runtime_ArrayConcat(Arguments args) {
5288 ASSERT(args.length() == 1);
5289 HandleScope handle_scope;
5290
5291 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5292 Handle<JSArray> arguments(arg_arrays);
5293
5294 // Pass 1: estimate the number of elements of the result
5295 // (it could be more than real numbers if prototype has elements).
5296 uint32_t result_length = 0;
5297 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5298
5299 { AssertNoAllocation nogc;
5300 for (uint32_t i = 0; i < num_of_args; i++) {
5301 Object* obj = arguments->GetElement(i);
5302 if (obj->IsJSArray()) {
5303 result_length +=
5304 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5305 } else {
5306 result_length++;
5307 }
5308 }
5309 }
5310
5311 // Allocate an empty array, will set length and content later.
5312 Handle<JSArray> result = Factory::NewJSArray(0);
5313
5314 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5315 // If estimated number of elements is more than half of length, a
5316 // fixed array (fast case) is more time and space-efficient than a
5317 // dictionary.
5318 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5319
5320 Handle<FixedArray> storage;
5321 if (fast_case) {
5322 // The backing storage array must have non-existing elements to
5323 // preserve holes across concat operations.
5324 storage = Factory::NewFixedArrayWithHoles(result_length);
5325
5326 } else {
5327 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5328 uint32_t at_least_space_for = estimate_nof_elements +
5329 (estimate_nof_elements >> 2);
5330 storage = Handle<FixedArray>::cast(
5331 Factory::NewDictionary(at_least_space_for));
5332 }
5333
5334 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5335
5336 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5337
5338 IterateArguments(arguments, &visitor);
5339
5340 result->set_length(*len);
5341 result->set_elements(*storage);
5342
5343 return *result;
5344}
5345
5346
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005347// This will not allocate (flatten the string), but it may run
5348// very slowly for very deeply nested ConsStrings. For debugging use only.
5349static Object* Runtime_GlobalPrint(Arguments args) {
5350 NoHandleAllocation ha;
5351 ASSERT(args.length() == 1);
5352
5353 CONVERT_CHECKED(String, string, args[0]);
5354 StringInputBuffer buffer(string);
5355 while (buffer.has_more()) {
5356 uint16_t character = buffer.GetNext();
5357 PrintF("%c", character);
5358 }
5359 return string;
5360}
5361
ager@chromium.org5ec48922009-05-05 07:25:34 +00005362// Moves all own elements of an object, that are below a limit, to positions
5363// starting at zero. All undefined values are placed after non-undefined values,
5364// and are followed by non-existing element. Does not change the length
5365// property.
5366// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005367static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005368 ASSERT(args.length() == 2);
5369 CONVERT_CHECKED(JSObject, object, args[0]);
5370 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5371 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005372}
5373
5374
5375// Move contents of argument 0 (an array) to argument 1 (an array)
5376static Object* Runtime_MoveArrayContents(Arguments args) {
5377 ASSERT(args.length() == 2);
5378 CONVERT_CHECKED(JSArray, from, args[0]);
5379 CONVERT_CHECKED(JSArray, to, args[1]);
5380 to->SetContent(FixedArray::cast(from->elements()));
5381 to->set_length(from->length());
5382 from->SetContent(Heap::empty_fixed_array());
5383 from->set_length(0);
5384 return to;
5385}
5386
5387
5388// How many elements does this array have?
5389static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5390 ASSERT(args.length() == 1);
5391 CONVERT_CHECKED(JSArray, array, args[0]);
5392 HeapObject* elements = array->elements();
5393 if (elements->IsDictionary()) {
5394 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5395 } else {
5396 return array->length();
5397 }
5398}
5399
5400
5401// Returns an array that tells you where in the [0, length) interval an array
5402// might have elements. Can either return keys or intervals. Keys can have
5403// gaps in (undefined). Intervals can also span over some undefined keys.
5404static Object* Runtime_GetArrayKeys(Arguments args) {
5405 ASSERT(args.length() == 2);
5406 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005407 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005408 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005409 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005410 // Create an array and get all the keys into it, then remove all the
5411 // keys that are not integers in the range 0 to length-1.
5412 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5413 int keys_length = keys->length();
5414 for (int i = 0; i < keys_length; i++) {
5415 Object* key = keys->get(i);
5416 uint32_t index;
5417 if (!Array::IndexFromObject(key, &index) || index >= length) {
5418 // Zap invalid keys.
5419 keys->set_undefined(i);
5420 }
5421 }
5422 return *Factory::NewJSArrayWithElements(keys);
5423 } else {
5424 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5425 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005426 single_interval->set(0,
5427 Smi::FromInt(-1),
5428 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005429 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5430 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005431 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005432 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005433 single_interval->set(1, *length_object);
5434 return *Factory::NewJSArrayWithElements(single_interval);
5435 }
5436}
5437
5438
5439// DefineAccessor takes an optional final argument which is the
5440// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5441// to the way accessors are implemented, it is set for both the getter
5442// and setter on the first call to DefineAccessor and ignored on
5443// subsequent calls.
5444static Object* Runtime_DefineAccessor(Arguments args) {
5445 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5446 // Compute attributes.
5447 PropertyAttributes attributes = NONE;
5448 if (args.length() == 5) {
5449 CONVERT_CHECKED(Smi, attrs, args[4]);
5450 int value = attrs->value();
5451 // Only attribute bits should be set.
5452 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5453 attributes = static_cast<PropertyAttributes>(value);
5454 }
5455
5456 CONVERT_CHECKED(JSObject, obj, args[0]);
5457 CONVERT_CHECKED(String, name, args[1]);
5458 CONVERT_CHECKED(Smi, flag, args[2]);
5459 CONVERT_CHECKED(JSFunction, fun, args[3]);
5460 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5461}
5462
5463
5464static Object* Runtime_LookupAccessor(Arguments args) {
5465 ASSERT(args.length() == 3);
5466 CONVERT_CHECKED(JSObject, obj, args[0]);
5467 CONVERT_CHECKED(String, name, args[1]);
5468 CONVERT_CHECKED(Smi, flag, args[2]);
5469 return obj->LookupAccessor(name, flag->value() == 0);
5470}
5471
5472
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005473#ifdef ENABLE_DEBUGGER_SUPPORT
5474static Object* Runtime_DebugBreak(Arguments args) {
5475 ASSERT(args.length() == 0);
5476 return Execution::DebugBreakHelper();
5477}
5478
5479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005480// Helper functions for wrapping and unwrapping stack frame ids.
5481static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005482 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005483 return Smi::FromInt(id >> 2);
5484}
5485
5486
5487static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5488 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5489}
5490
5491
5492// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005493// args[0]: debug event listener function to set or null or undefined for
5494// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005496static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005497 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005498 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5499 args[0]->IsUndefined() ||
5500 args[0]->IsNull());
5501 Handle<Object> callback = args.at<Object>(0);
5502 Handle<Object> data = args.at<Object>(1);
5503 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005504
5505 return Heap::undefined_value();
5506}
5507
5508
5509static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005510 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511 StackGuard::DebugBreak();
5512 return Heap::undefined_value();
5513}
5514
5515
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005516// Find the length of the prototype chain that is to to handled as one. If a
5517// prototype object is hidden it is to be viewed as part of the the object it
5518// is prototype for.
5519static int LocalPrototypeChainLength(JSObject* obj) {
5520 int count = 1;
5521 Object* proto = obj->GetPrototype();
5522 while (proto->IsJSObject() &&
5523 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5524 count++;
5525 proto = JSObject::cast(proto)->GetPrototype();
5526 }
5527 return count;
5528}
5529
5530
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005531static Object* DebugLookupResultValue(Object* receiver, String* name,
5532 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005533 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005534 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005535 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005536 case NORMAL: {
5537 Dictionary* dict =
5538 JSObject::cast(result->holder())->property_dictionary();
5539 value = dict->ValueAt(result->GetDictionaryEntry());
5540 if (value->IsTheHole()) {
5541 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542 }
5543 return value;
5544 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005545 case FIELD:
5546 value =
5547 JSObject::cast(
5548 result->holder())->FastPropertyAt(result->GetFieldIndex());
5549 if (value->IsTheHole()) {
5550 return Heap::undefined_value();
5551 }
5552 return value;
5553 case CONSTANT_FUNCTION:
5554 return result->GetConstantFunction();
5555 case CALLBACKS: {
5556 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005557 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005558 value = receiver->GetPropertyWithCallback(
5559 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005560 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005561 value = Top::pending_exception();
5562 Top::clear_pending_exception();
5563 if (caught_exception != NULL) {
5564 *caught_exception = true;
5565 }
5566 }
5567 return value;
5568 } else {
5569 return Heap::undefined_value();
5570 }
5571 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005572 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005573 case MAP_TRANSITION:
5574 case CONSTANT_TRANSITION:
5575 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005576 return Heap::undefined_value();
5577 default:
5578 UNREACHABLE();
5579 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005580 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005581 return Heap::undefined_value();
5582}
5583
5584
ager@chromium.org32912102009-01-16 10:38:43 +00005585// Get debugger related details for an object property.
5586// args[0]: object holding property
5587// args[1]: name of the property
5588//
5589// The array returned contains the following information:
5590// 0: Property value
5591// 1: Property details
5592// 2: Property value is exception
5593// 3: Getter function if defined
5594// 4: Setter function if defined
5595// Items 2-4 are only filled if the property has either a getter or a setter
5596// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005597static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005598 HandleScope scope;
5599
5600 ASSERT(args.length() == 2);
5601
5602 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5603 CONVERT_ARG_CHECKED(String, name, 1);
5604
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005605 // Make sure to set the current context to the context before the debugger was
5606 // entered (if the debugger is entered). The reason for switching context here
5607 // is that for some property lookups (accessors and interceptors) callbacks
5608 // into the embedding application can occour, and the embedding application
5609 // could have the assumption that its own global context is the current
5610 // context and not some internal debugger context.
5611 SaveContext save;
5612 if (Debug::InDebugger()) {
5613 Top::set_context(*Debug::debugger_entry()->GetContext());
5614 }
5615
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005616 // Skip the global proxy as it has no properties and always delegates to the
5617 // real global object.
5618 if (obj->IsJSGlobalProxy()) {
5619 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5620 }
5621
5622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005623 // Check if the name is trivially convertible to an index and get the element
5624 // if so.
5625 uint32_t index;
5626 if (name->AsArrayIndex(&index)) {
5627 Handle<FixedArray> details = Factory::NewFixedArray(2);
5628 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5629 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5630 return *Factory::NewJSArrayWithElements(details);
5631 }
5632
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005633 // Find the number of objects making up this.
5634 int length = LocalPrototypeChainLength(*obj);
5635
5636 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005637 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005638 Handle<JSObject> jsproto = obj;
5639 for (int i = 0; i < length; i++) {
5640 jsproto->LocalLookup(*name, &result);
5641 if (result.IsProperty()) {
5642 break;
5643 }
5644 if (i < length - 1) {
5645 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5646 }
5647 }
5648
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005649 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005650 // LookupResult is not GC safe as all its members are raw object pointers.
5651 // When calling DebugLookupResultValue GC can happen as this might invoke
5652 // callbacks. After the call to DebugLookupResultValue the callback object
5653 // in the LookupResult might still be needed. Put it into a handle for later
5654 // use.
5655 PropertyType result_type = result.type();
5656 Handle<Object> result_callback_obj;
5657 if (result_type == CALLBACKS) {
5658 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5659 }
5660
5661 // Find the actual value. Don't use result after this call as it's content
5662 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005663 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005664 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005665 &caught_exception);
5666 if (value->IsFailure()) return value;
5667 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005668
ager@chromium.org32912102009-01-16 10:38:43 +00005669 // If the callback object is a fixed array then it contains JavaScript
5670 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005671 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5672 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005673 Handle<FixedArray> details =
5674 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005675 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005676 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005677 if (hasJavaScriptAccessors) {
5678 details->set(2,
5679 caught_exception ? Heap::true_value() : Heap::false_value());
5680 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5681 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5682 }
5683
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005684 return *Factory::NewJSArrayWithElements(details);
5685 }
5686 return Heap::undefined_value();
5687}
5688
5689
5690static Object* Runtime_DebugGetProperty(Arguments args) {
5691 HandleScope scope;
5692
5693 ASSERT(args.length() == 2);
5694
5695 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5696 CONVERT_ARG_CHECKED(String, name, 1);
5697
5698 LookupResult result;
5699 obj->Lookup(*name, &result);
5700 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005701 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005702 }
5703 return Heap::undefined_value();
5704}
5705
5706
5707// Return the names of the local named properties.
5708// args[0]: object
5709static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5710 HandleScope scope;
5711 ASSERT(args.length() == 1);
5712 if (!args[0]->IsJSObject()) {
5713 return Heap::undefined_value();
5714 }
5715 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5716
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005717 // Skip the global proxy as it has no properties and always delegates to the
5718 // real global object.
5719 if (obj->IsJSGlobalProxy()) {
5720 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5721 }
5722
5723 // Find the number of objects making up this.
5724 int length = LocalPrototypeChainLength(*obj);
5725
5726 // Find the number of local properties for each of the objects.
5727 int* local_property_count = NewArray<int>(length);
5728 int total_property_count = 0;
5729 Handle<JSObject> jsproto = obj;
5730 for (int i = 0; i < length; i++) {
5731 int n;
5732 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5733 local_property_count[i] = n;
5734 total_property_count += n;
5735 if (i < length - 1) {
5736 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5737 }
5738 }
5739
5740 // Allocate an array with storage for all the property names.
5741 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5742
5743 // Get the property names.
5744 jsproto = obj;
5745 for (int i = 0; i < length; i++) {
5746 jsproto->GetLocalPropertyNames(*names,
5747 i == 0 ? 0 : local_property_count[i - 1]);
5748 if (i < length - 1) {
5749 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5750 }
5751 }
5752
5753 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005754 return *Factory::NewJSArrayWithElements(names);
5755}
5756
5757
5758// Return the names of the local indexed properties.
5759// args[0]: object
5760static Object* Runtime_DebugLocalElementNames(Arguments args) {
5761 HandleScope scope;
5762 ASSERT(args.length() == 1);
5763 if (!args[0]->IsJSObject()) {
5764 return Heap::undefined_value();
5765 }
5766 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5767
5768 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5769 Handle<FixedArray> names = Factory::NewFixedArray(n);
5770 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5771 return *Factory::NewJSArrayWithElements(names);
5772}
5773
5774
5775// Return the property type calculated from the property details.
5776// args[0]: smi with property details.
5777static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5778 ASSERT(args.length() == 1);
5779 CONVERT_CHECKED(Smi, details, args[0]);
5780 PropertyType type = PropertyDetails(details).type();
5781 return Smi::FromInt(static_cast<int>(type));
5782}
5783
5784
5785// Return the property attribute calculated from the property details.
5786// args[0]: smi with property details.
5787static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5788 ASSERT(args.length() == 1);
5789 CONVERT_CHECKED(Smi, details, args[0]);
5790 PropertyAttributes attributes = PropertyDetails(details).attributes();
5791 return Smi::FromInt(static_cast<int>(attributes));
5792}
5793
5794
5795// Return the property insertion index calculated from the property details.
5796// args[0]: smi with property details.
5797static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5798 ASSERT(args.length() == 1);
5799 CONVERT_CHECKED(Smi, details, args[0]);
5800 int index = PropertyDetails(details).index();
5801 return Smi::FromInt(index);
5802}
5803
5804
5805// Return information on whether an object has a named or indexed interceptor.
5806// args[0]: object
5807static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5808 HandleScope scope;
5809 ASSERT(args.length() == 1);
5810 if (!args[0]->IsJSObject()) {
5811 return Smi::FromInt(0);
5812 }
5813 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5814
5815 int result = 0;
5816 if (obj->HasNamedInterceptor()) result |= 2;
5817 if (obj->HasIndexedInterceptor()) result |= 1;
5818
5819 return Smi::FromInt(result);
5820}
5821
5822
5823// Return property names from named interceptor.
5824// args[0]: object
5825static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5826 HandleScope scope;
5827 ASSERT(args.length() == 1);
5828 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005829
ager@chromium.org32912102009-01-16 10:38:43 +00005830 if (obj->HasNamedInterceptor()) {
5831 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5832 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5833 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005834 return Heap::undefined_value();
5835}
5836
5837
5838// Return element names from indexed interceptor.
5839// args[0]: object
5840static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5841 HandleScope scope;
5842 ASSERT(args.length() == 1);
5843 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005844
ager@chromium.org32912102009-01-16 10:38:43 +00005845 if (obj->HasIndexedInterceptor()) {
5846 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5847 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5848 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005849 return Heap::undefined_value();
5850}
5851
5852
5853// Return property value from named interceptor.
5854// args[0]: object
5855// args[1]: property name
5856static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5857 HandleScope scope;
5858 ASSERT(args.length() == 2);
5859 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5860 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5861 CONVERT_ARG_CHECKED(String, name, 1);
5862
5863 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005864 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005865}
5866
5867
5868// Return element value from indexed interceptor.
5869// args[0]: object
5870// args[1]: index
5871static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5872 HandleScope scope;
5873 ASSERT(args.length() == 2);
5874 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5875 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5876 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5877
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005878 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005879}
5880
5881
5882static Object* Runtime_CheckExecutionState(Arguments args) {
5883 ASSERT(args.length() >= 1);
5884 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005885 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005886 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887 return Top::Throw(Heap::illegal_execution_state_symbol());
5888 }
5889
5890 return Heap::true_value();
5891}
5892
5893
5894static Object* Runtime_GetFrameCount(Arguments args) {
5895 HandleScope scope;
5896 ASSERT(args.length() == 1);
5897
5898 // Check arguments.
5899 Object* result = Runtime_CheckExecutionState(args);
5900 if (result->IsFailure()) return result;
5901
5902 // Count all frames which are relevant to debugging stack trace.
5903 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005904 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005905 if (id == StackFrame::NO_ID) {
5906 // If there is no JavaScript stack frame count is 0.
5907 return Smi::FromInt(0);
5908 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005909 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5910 return Smi::FromInt(n);
5911}
5912
5913
5914static const int kFrameDetailsFrameIdIndex = 0;
5915static const int kFrameDetailsReceiverIndex = 1;
5916static const int kFrameDetailsFunctionIndex = 2;
5917static const int kFrameDetailsArgumentCountIndex = 3;
5918static const int kFrameDetailsLocalCountIndex = 4;
5919static const int kFrameDetailsSourcePositionIndex = 5;
5920static const int kFrameDetailsConstructCallIndex = 6;
5921static const int kFrameDetailsDebuggerFrameIndex = 7;
5922static const int kFrameDetailsFirstDynamicIndex = 8;
5923
5924// Return an array with frame details
5925// args[0]: number: break id
5926// args[1]: number: frame index
5927//
5928// The array returned contains the following information:
5929// 0: Frame id
5930// 1: Receiver
5931// 2: Function
5932// 3: Argument count
5933// 4: Local count
5934// 5: Source position
5935// 6: Constructor call
5936// 7: Debugger frame
5937// Arguments name, value
5938// Locals name, value
5939static Object* Runtime_GetFrameDetails(Arguments args) {
5940 HandleScope scope;
5941 ASSERT(args.length() == 2);
5942
5943 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005944 Object* check = Runtime_CheckExecutionState(args);
5945 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005946 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5947
5948 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005949 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005950 if (id == StackFrame::NO_ID) {
5951 // If there are no JavaScript stack frames return undefined.
5952 return Heap::undefined_value();
5953 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005954 int count = 0;
5955 JavaScriptFrameIterator it(id);
5956 for (; !it.done(); it.Advance()) {
5957 if (count == index) break;
5958 count++;
5959 }
5960 if (it.done()) return Heap::undefined_value();
5961
5962 // Traverse the saved contexts chain to find the active context for the
5963 // selected frame.
5964 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005965 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005966 save = save->prev();
5967 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005968 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005969
5970 // Get the frame id.
5971 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5972
5973 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005974 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005975
5976 // Check for constructor frame.
5977 bool constructor = it.frame()->IsConstructor();
5978
5979 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005980 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005981 ScopeInfo<> info(*code);
5982
5983 // Get the context.
5984 Handle<Context> context(Context::cast(it.frame()->context()));
5985
5986 // Get the locals names and values into a temporary array.
5987 //
5988 // TODO(1240907): Hide compiler-introduced stack variables
5989 // (e.g. .result)? For users of the debugger, they will probably be
5990 // confusing.
5991 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5992 for (int i = 0; i < info.NumberOfLocals(); i++) {
5993 // Name of the local.
5994 locals->set(i * 2, *info.LocalName(i));
5995
5996 // Fetch the value of the local - either from the stack or from a
5997 // heap-allocated context.
5998 if (i < info.number_of_stack_slots()) {
5999 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6000 } else {
6001 Handle<String> name = info.LocalName(i);
6002 // Traverse the context chain to the function context as all local
6003 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006004 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006005 context = Handle<Context>(context->previous());
6006 }
6007 ASSERT(context->is_function_context());
6008 locals->set(i * 2 + 1,
6009 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6010 NULL)));
6011 }
6012 }
6013
6014 // Now advance to the arguments adapter frame (if any). If contains all
6015 // the provided parameters and
6016
6017 // Now advance to the arguments adapter frame (if any). It contains all
6018 // the provided parameters whereas the function frame always have the number
6019 // of arguments matching the functions parameters. The rest of the
6020 // information (except for what is collected above) is the same.
6021 it.AdvanceToArgumentsFrame();
6022
6023 // Find the number of arguments to fill. At least fill the number of
6024 // parameters for the function and fill more if more parameters are provided.
6025 int argument_count = info.number_of_parameters();
6026 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6027 argument_count = it.frame()->GetProvidedParametersCount();
6028 }
6029
6030 // Calculate the size of the result.
6031 int details_size = kFrameDetailsFirstDynamicIndex +
6032 2 * (argument_count + info.NumberOfLocals());
6033 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6034
6035 // Add the frame id.
6036 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6037
6038 // Add the function (same as in function frame).
6039 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6040
6041 // Add the arguments count.
6042 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6043
6044 // Add the locals count
6045 details->set(kFrameDetailsLocalCountIndex,
6046 Smi::FromInt(info.NumberOfLocals()));
6047
6048 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006049 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006050 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6051 } else {
6052 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6053 }
6054
6055 // Add the constructor information.
6056 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6057
6058 // Add information on whether this frame is invoked in the debugger context.
6059 details->set(kFrameDetailsDebuggerFrameIndex,
6060 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6061
6062 // Fill the dynamic part.
6063 int details_index = kFrameDetailsFirstDynamicIndex;
6064
6065 // Add arguments name and value.
6066 for (int i = 0; i < argument_count; i++) {
6067 // Name of the argument.
6068 if (i < info.number_of_parameters()) {
6069 details->set(details_index++, *info.parameter_name(i));
6070 } else {
6071 details->set(details_index++, Heap::undefined_value());
6072 }
6073
6074 // Parameter value.
6075 if (i < it.frame()->GetProvidedParametersCount()) {
6076 details->set(details_index++, it.frame()->GetParameter(i));
6077 } else {
6078 details->set(details_index++, Heap::undefined_value());
6079 }
6080 }
6081
6082 // Add locals name and value from the temporary copy from the function frame.
6083 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6084 details->set(details_index++, locals->get(i));
6085 }
6086
6087 // Add the receiver (same as in function frame).
6088 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6089 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6090 Handle<Object> receiver(it.frame()->receiver());
6091 if (!receiver->IsJSObject()) {
6092 // If the receiver is NOT a JSObject we have hit an optimization
6093 // where a value object is not converted into a wrapped JS objects.
6094 // To hide this optimization from the debugger, we wrap the receiver
6095 // by creating correct wrapper object based on the calling frame's
6096 // global context.
6097 it.Advance();
6098 Handle<Context> calling_frames_global_context(
6099 Context::cast(Context::cast(it.frame()->context())->global_context()));
6100 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6101 }
6102 details->set(kFrameDetailsReceiverIndex, *receiver);
6103
6104 ASSERT_EQ(details_size, details_index);
6105 return *Factory::NewJSArrayWithElements(details);
6106}
6107
6108
6109static Object* Runtime_GetCFrames(Arguments args) {
6110 HandleScope scope;
6111 ASSERT(args.length() == 1);
6112 Object* result = Runtime_CheckExecutionState(args);
6113 if (result->IsFailure()) return result;
6114
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006115#if V8_HOST_ARCH_64_BIT
6116 UNIMPLEMENTED();
6117 return Heap::undefined_value();
6118#else
6119
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006120 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006121 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6122 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006123 if (frames_count == OS::kStackWalkError) {
6124 return Heap::undefined_value();
6125 }
6126
6127 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6128 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6129 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6130 for (int i = 0; i < frames_count; i++) {
6131 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6132 frame_value->SetProperty(
6133 *address_str,
6134 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6135 NONE);
6136
6137 // Get the stack walk text for this frame.
6138 Handle<String> frame_text;
6139 if (strlen(frames[i].text) > 0) {
6140 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6141 frame_text = Factory::NewStringFromAscii(str);
6142 }
6143
6144 if (!frame_text.is_null()) {
6145 frame_value->SetProperty(*text_str, *frame_text, NONE);
6146 }
6147
6148 frames_array->set(i, *frame_value);
6149 }
6150 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006151#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006152}
6153
6154
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006155static Object* Runtime_GetThreadCount(Arguments args) {
6156 HandleScope scope;
6157 ASSERT(args.length() == 1);
6158
6159 // Check arguments.
6160 Object* result = Runtime_CheckExecutionState(args);
6161 if (result->IsFailure()) return result;
6162
6163 // Count all archived V8 threads.
6164 int n = 0;
6165 for (ThreadState* thread = ThreadState::FirstInUse();
6166 thread != NULL;
6167 thread = thread->Next()) {
6168 n++;
6169 }
6170
6171 // Total number of threads is current thread and archived threads.
6172 return Smi::FromInt(n + 1);
6173}
6174
6175
6176static const int kThreadDetailsCurrentThreadIndex = 0;
6177static const int kThreadDetailsThreadIdIndex = 1;
6178static const int kThreadDetailsSize = 2;
6179
6180// Return an array with thread details
6181// args[0]: number: break id
6182// args[1]: number: thread index
6183//
6184// The array returned contains the following information:
6185// 0: Is current thread?
6186// 1: Thread id
6187static Object* Runtime_GetThreadDetails(Arguments args) {
6188 HandleScope scope;
6189 ASSERT(args.length() == 2);
6190
6191 // Check arguments.
6192 Object* check = Runtime_CheckExecutionState(args);
6193 if (check->IsFailure()) return check;
6194 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6195
6196 // Allocate array for result.
6197 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6198
6199 // Thread index 0 is current thread.
6200 if (index == 0) {
6201 // Fill the details.
6202 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6203 details->set(kThreadDetailsThreadIdIndex,
6204 Smi::FromInt(ThreadManager::CurrentId()));
6205 } else {
6206 // Find the thread with the requested index.
6207 int n = 1;
6208 ThreadState* thread = ThreadState::FirstInUse();
6209 while (index != n && thread != NULL) {
6210 thread = thread->Next();
6211 n++;
6212 }
6213 if (thread == NULL) {
6214 return Heap::undefined_value();
6215 }
6216
6217 // Fill the details.
6218 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6219 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6220 }
6221
6222 // Convert to JS array and return.
6223 return *Factory::NewJSArrayWithElements(details);
6224}
6225
6226
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006227static Object* Runtime_GetBreakLocations(Arguments args) {
6228 HandleScope scope;
6229 ASSERT(args.length() == 1);
6230
6231 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6232 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6233 // Find the number of break points
6234 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6235 if (break_locations->IsUndefined()) return Heap::undefined_value();
6236 // Return array as JS array
6237 return *Factory::NewJSArrayWithElements(
6238 Handle<FixedArray>::cast(break_locations));
6239}
6240
6241
6242// Set a break point in a function
6243// args[0]: function
6244// args[1]: number: break source position (within the function source)
6245// args[2]: number: break point object
6246static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6247 HandleScope scope;
6248 ASSERT(args.length() == 3);
6249 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6250 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6251 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6252 RUNTIME_ASSERT(source_position >= 0);
6253 Handle<Object> break_point_object_arg = args.at<Object>(2);
6254
6255 // Set break point.
6256 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6257
6258 return Heap::undefined_value();
6259}
6260
6261
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006262Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6263 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006264 // Iterate the heap looking for SharedFunctionInfo generated from the
6265 // script. The inner most SharedFunctionInfo containing the source position
6266 // for the requested break point is found.
6267 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6268 // which is found is not compiled it is compiled and the heap is iterated
6269 // again as the compilation might create inner functions from the newly
6270 // compiled function and the actual requested break point might be in one of
6271 // these functions.
6272 bool done = false;
6273 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006274 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006275 Handle<SharedFunctionInfo> target;
6276 // The current candidate for the last function in script:
6277 Handle<SharedFunctionInfo> last;
6278 while (!done) {
6279 HeapIterator iterator;
6280 while (iterator.has_next()) {
6281 HeapObject* obj = iterator.next();
6282 ASSERT(obj != NULL);
6283 if (obj->IsSharedFunctionInfo()) {
6284 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6285 if (shared->script() == *script) {
6286 // If the SharedFunctionInfo found has the requested script data and
6287 // contains the source position it is a candidate.
6288 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006289 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006290 start_position = shared->start_position();
6291 }
6292 if (start_position <= position &&
6293 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006294 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006295 // candidate this is the new candidate.
6296 if (target.is_null()) {
6297 target_start_position = start_position;
6298 target = shared;
6299 } else {
6300 if (target_start_position < start_position &&
6301 shared->end_position() < target->end_position()) {
6302 target_start_position = start_position;
6303 target = shared;
6304 }
6305 }
6306 }
6307
6308 // Keep track of the last function in the script.
6309 if (last.is_null() ||
6310 shared->end_position() > last->start_position()) {
6311 last = shared;
6312 }
6313 }
6314 }
6315 }
6316
6317 // Make sure some candidate is selected.
6318 if (target.is_null()) {
6319 if (!last.is_null()) {
6320 // Position after the last function - use last.
6321 target = last;
6322 } else {
6323 // Unable to find function - possibly script without any function.
6324 return Heap::undefined_value();
6325 }
6326 }
6327
6328 // If the candidate found is compiled we are done. NOTE: when lazy
6329 // compilation of inner functions is introduced some additional checking
6330 // needs to be done here to compile inner functions.
6331 done = target->is_compiled();
6332 if (!done) {
6333 // If the candidate is not compiled compile it to reveal any inner
6334 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006335 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006336 }
6337 }
6338
6339 return *target;
6340}
6341
6342
6343// Change the state of a break point in a script. NOTE: Regarding performance
6344// see the NOTE for GetScriptFromScriptData.
6345// args[0]: script to set break point in
6346// args[1]: number: break source position (within the script source)
6347// args[2]: number: break point object
6348static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6349 HandleScope scope;
6350 ASSERT(args.length() == 3);
6351 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6352 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6353 RUNTIME_ASSERT(source_position >= 0);
6354 Handle<Object> break_point_object_arg = args.at<Object>(2);
6355
6356 // Get the script from the script wrapper.
6357 RUNTIME_ASSERT(wrapper->value()->IsScript());
6358 Handle<Script> script(Script::cast(wrapper->value()));
6359
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006360 Object* result = Runtime::FindSharedFunctionInfoInScript(
6361 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006362 if (!result->IsUndefined()) {
6363 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6364 // Find position within function. The script position might be before the
6365 // source position of the first function.
6366 int position;
6367 if (shared->start_position() > source_position) {
6368 position = 0;
6369 } else {
6370 position = source_position - shared->start_position();
6371 }
6372 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6373 }
6374 return Heap::undefined_value();
6375}
6376
6377
6378// Clear a break point
6379// args[0]: number: break point object
6380static Object* Runtime_ClearBreakPoint(Arguments args) {
6381 HandleScope scope;
6382 ASSERT(args.length() == 1);
6383 Handle<Object> break_point_object_arg = args.at<Object>(0);
6384
6385 // Clear break point.
6386 Debug::ClearBreakPoint(break_point_object_arg);
6387
6388 return Heap::undefined_value();
6389}
6390
6391
6392// Change the state of break on exceptions
6393// args[0]: boolean indicating uncaught exceptions
6394// args[1]: boolean indicating on/off
6395static Object* Runtime_ChangeBreakOnException(Arguments args) {
6396 HandleScope scope;
6397 ASSERT(args.length() == 2);
6398 ASSERT(args[0]->IsNumber());
6399 ASSERT(args[1]->IsBoolean());
6400
6401 // Update break point state
6402 ExceptionBreakType type =
6403 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6404 bool enable = args[1]->ToBoolean()->IsTrue();
6405 Debug::ChangeBreakOnException(type, enable);
6406 return Heap::undefined_value();
6407}
6408
6409
6410// Prepare for stepping
6411// args[0]: break id for checking execution state
6412// args[1]: step action from the enumeration StepAction
6413// args[2]: number of times to perform the step
6414static Object* Runtime_PrepareStep(Arguments args) {
6415 HandleScope scope;
6416 ASSERT(args.length() == 3);
6417 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006418 Object* check = Runtime_CheckExecutionState(args);
6419 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006420 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6421 return Top::Throw(Heap::illegal_argument_symbol());
6422 }
6423
6424 // Get the step action and check validity.
6425 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6426 if (step_action != StepIn &&
6427 step_action != StepNext &&
6428 step_action != StepOut &&
6429 step_action != StepInMin &&
6430 step_action != StepMin) {
6431 return Top::Throw(Heap::illegal_argument_symbol());
6432 }
6433
6434 // Get the number of steps.
6435 int step_count = NumberToInt32(args[2]);
6436 if (step_count < 1) {
6437 return Top::Throw(Heap::illegal_argument_symbol());
6438 }
6439
6440 // Prepare step.
6441 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6442 return Heap::undefined_value();
6443}
6444
6445
6446// Clear all stepping set by PrepareStep.
6447static Object* Runtime_ClearStepping(Arguments args) {
6448 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006449 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006450 Debug::ClearStepping();
6451 return Heap::undefined_value();
6452}
6453
6454
6455// Creates a copy of the with context chain. The copy of the context chain is
6456// is linked to the function context supplied.
6457static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6458 Handle<Context> function_context) {
6459 // At the bottom of the chain. Return the function context to link to.
6460 if (context_chain->is_function_context()) {
6461 return function_context;
6462 }
6463
6464 // Recursively copy the with contexts.
6465 Handle<Context> previous(context_chain->previous());
6466 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6467 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006468 CopyWithContextChain(function_context, previous),
6469 extension,
6470 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006471}
6472
6473
6474// Helper function to find or create the arguments object for
6475// Runtime_DebugEvaluate.
6476static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6477 Handle<JSFunction> function,
6478 Handle<Code> code,
6479 const ScopeInfo<>* sinfo,
6480 Handle<Context> function_context) {
6481 // Try to find the value of 'arguments' to pass as parameter. If it is not
6482 // found (that is the debugged function does not reference 'arguments' and
6483 // does not support eval) then create an 'arguments' object.
6484 int index;
6485 if (sinfo->number_of_stack_slots() > 0) {
6486 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6487 if (index != -1) {
6488 return Handle<Object>(frame->GetExpression(index));
6489 }
6490 }
6491
6492 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6493 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6494 NULL);
6495 if (index != -1) {
6496 return Handle<Object>(function_context->get(index));
6497 }
6498 }
6499
6500 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006501 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6502 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006503 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006504 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006505 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006506 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006507 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006508 return arguments;
6509}
6510
6511
6512// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006513// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006514// extension part has all the parameters and locals of the function on the
6515// stack frame. A function which calls eval with the code to evaluate is then
6516// compiled in this context and called in this context. As this context
6517// replaces the context of the function on the stack frame a new (empty)
6518// function is created as well to be used as the closure for the context.
6519// This function and the context acts as replacements for the function on the
6520// stack frame presenting the same view of the values of parameters and
6521// local variables as if the piece of JavaScript was evaluated at the point
6522// where the function on the stack frame is currently stopped.
6523static Object* Runtime_DebugEvaluate(Arguments args) {
6524 HandleScope scope;
6525
6526 // Check the execution state and decode arguments frame and source to be
6527 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006528 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006529 Object* check_result = Runtime_CheckExecutionState(args);
6530 if (check_result->IsFailure()) return check_result;
6531 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6532 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006533 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6534
6535 // Handle the processing of break.
6536 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006537
6538 // Get the frame where the debugging is performed.
6539 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6540 JavaScriptFrameIterator it(id);
6541 JavaScriptFrame* frame = it.frame();
6542 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6543 Handle<Code> code(function->code());
6544 ScopeInfo<> sinfo(*code);
6545
6546 // Traverse the saved contexts chain to find the active context for the
6547 // selected frame.
6548 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006549 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006550 save = save->prev();
6551 }
6552 ASSERT(save != NULL);
6553 SaveContext savex;
6554 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006555
6556 // Create the (empty) function replacing the function on the stack frame for
6557 // the purpose of evaluating in the context created below. It is important
6558 // that this function does not describe any parameters and local variables
6559 // in the context. If it does then this will cause problems with the lookup
6560 // in Context::Lookup, where context slots for parameters and local variables
6561 // are looked at before the extension object.
6562 Handle<JSFunction> go_between =
6563 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6564 go_between->set_context(function->context());
6565#ifdef DEBUG
6566 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6567 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6568 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6569#endif
6570
6571 // Allocate and initialize a context extension object with all the
6572 // arguments, stack locals heap locals and extension properties of the
6573 // debugged function.
6574 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6575 // First fill all parameters to the context extension.
6576 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6577 SetProperty(context_ext,
6578 sinfo.parameter_name(i),
6579 Handle<Object>(frame->GetParameter(i)), NONE);
6580 }
6581 // Second fill all stack locals to the context extension.
6582 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6583 SetProperty(context_ext,
6584 sinfo.stack_slot_name(i),
6585 Handle<Object>(frame->GetExpression(i)), NONE);
6586 }
6587 // Third fill all context locals to the context extension.
6588 Handle<Context> frame_context(Context::cast(frame->context()));
6589 Handle<Context> function_context(frame_context->fcontext());
6590 for (int i = Context::MIN_CONTEXT_SLOTS;
6591 i < sinfo.number_of_context_slots();
6592 ++i) {
6593 int context_index =
6594 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6595 SetProperty(context_ext,
6596 sinfo.context_slot_name(i),
6597 Handle<Object>(function_context->get(context_index)), NONE);
6598 }
6599 // Finally copy any properties from the function context extension. This will
6600 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006601 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006602 !function_context->IsGlobalContext()) {
6603 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6604 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6605 for (int i = 0; i < keys->length(); i++) {
6606 // Names of variables introduced by eval are strings.
6607 ASSERT(keys->get(i)->IsString());
6608 Handle<String> key(String::cast(keys->get(i)));
6609 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6610 }
6611 }
6612
6613 // Allocate a new context for the debug evaluation and set the extension
6614 // object build.
6615 Handle<Context> context =
6616 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6617 context->set_extension(*context_ext);
6618 // Copy any with contexts present and chain them in front of this context.
6619 context = CopyWithContextChain(frame_context, context);
6620
6621 // Wrap the evaluation statement in a new function compiled in the newly
6622 // created context. The function has one parameter which has to be called
6623 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006624 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006625 // function(arguments,__source__) {return eval(__source__);}
6626 static const char* source_str =
6627 "function(arguments,__source__){return eval(__source__);}";
6628 static const int source_str_length = strlen(source_str);
6629 Handle<String> function_source =
6630 Factory::NewStringFromAscii(Vector<const char>(source_str,
6631 source_str_length));
6632 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006633 Compiler::CompileEval(function_source,
6634 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006635 context->IsGlobalContext(),
6636 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006637 if (boilerplate.is_null()) return Failure::Exception();
6638 Handle<JSFunction> compiled_function =
6639 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6640
6641 // Invoke the result of the compilation to get the evaluation function.
6642 bool has_pending_exception;
6643 Handle<Object> receiver(frame->receiver());
6644 Handle<Object> evaluation_function =
6645 Execution::Call(compiled_function, receiver, 0, NULL,
6646 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006647 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006648
6649 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6650 function_context);
6651
6652 // Invoke the evaluation function and return the result.
6653 const int argc = 2;
6654 Object** argv[argc] = { arguments.location(),
6655 Handle<Object>::cast(source).location() };
6656 Handle<Object> result =
6657 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6658 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006659 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006660 return *result;
6661}
6662
6663
6664static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6665 HandleScope scope;
6666
6667 // Check the execution state and decode arguments frame and source to be
6668 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006669 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006670 Object* check_result = Runtime_CheckExecutionState(args);
6671 if (check_result->IsFailure()) return check_result;
6672 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006673 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6674
6675 // Handle the processing of break.
6676 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006677
6678 // Enter the top context from before the debugger was invoked.
6679 SaveContext save;
6680 SaveContext* top = &save;
6681 while (top != NULL && *top->context() == *Debug::debug_context()) {
6682 top = top->prev();
6683 }
6684 if (top != NULL) {
6685 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006686 }
6687
6688 // Get the global context now set to the top context from before the
6689 // debugger was invoked.
6690 Handle<Context> context = Top::global_context();
6691
6692 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006693 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006694 Handle<JSFunction>(Compiler::CompileEval(source,
6695 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006696 true,
6697 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006698 if (boilerplate.is_null()) return Failure::Exception();
6699 Handle<JSFunction> compiled_function =
6700 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6701 context));
6702
6703 // Invoke the result of the compilation to get the evaluation function.
6704 bool has_pending_exception;
6705 Handle<Object> receiver = Top::global();
6706 Handle<Object> result =
6707 Execution::Call(compiled_function, receiver, 0, NULL,
6708 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006709 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006710 return *result;
6711}
6712
6713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006714static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6715 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006716 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006717
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006718 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006719 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006720
6721 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006722 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006723 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6724 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6725 // because using
6726 // instances->set(i, *GetScriptWrapper(script))
6727 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6728 // already have deferenced the instances handle.
6729 Handle<JSValue> wrapper = GetScriptWrapper(script);
6730 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006731 }
6732
6733 // Return result as a JS array.
6734 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6735 Handle<JSArray>::cast(result)->SetContent(*instances);
6736 return *result;
6737}
6738
6739
6740// Helper function used by Runtime_DebugReferencedBy below.
6741static int DebugReferencedBy(JSObject* target,
6742 Object* instance_filter, int max_references,
6743 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006744 JSFunction* arguments_function) {
6745 NoHandleAllocation ha;
6746 AssertNoAllocation no_alloc;
6747
6748 // Iterate the heap.
6749 int count = 0;
6750 JSObject* last = NULL;
6751 HeapIterator iterator;
6752 while (iterator.has_next() &&
6753 (max_references == 0 || count < max_references)) {
6754 // Only look at all JSObjects.
6755 HeapObject* heap_obj = iterator.next();
6756 if (heap_obj->IsJSObject()) {
6757 // Skip context extension objects and argument arrays as these are
6758 // checked in the context of functions using them.
6759 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006760 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006761 obj->map()->constructor() == arguments_function) {
6762 continue;
6763 }
6764
6765 // Check if the JS object has a reference to the object looked for.
6766 if (obj->ReferencesObject(target)) {
6767 // Check instance filter if supplied. This is normally used to avoid
6768 // references from mirror objects (see Runtime_IsInPrototypeChain).
6769 if (!instance_filter->IsUndefined()) {
6770 Object* V = obj;
6771 while (true) {
6772 Object* prototype = V->GetPrototype();
6773 if (prototype->IsNull()) {
6774 break;
6775 }
6776 if (instance_filter == prototype) {
6777 obj = NULL; // Don't add this object.
6778 break;
6779 }
6780 V = prototype;
6781 }
6782 }
6783
6784 if (obj != NULL) {
6785 // Valid reference found add to instance array if supplied an update
6786 // count.
6787 if (instances != NULL && count < instances_size) {
6788 instances->set(count, obj);
6789 }
6790 last = obj;
6791 count++;
6792 }
6793 }
6794 }
6795 }
6796
6797 // Check for circular reference only. This can happen when the object is only
6798 // referenced from mirrors and has a circular reference in which case the
6799 // object is not really alive and would have been garbage collected if not
6800 // referenced from the mirror.
6801 if (count == 1 && last == target) {
6802 count = 0;
6803 }
6804
6805 // Return the number of referencing objects found.
6806 return count;
6807}
6808
6809
6810// Scan the heap for objects with direct references to an object
6811// args[0]: the object to find references to
6812// args[1]: constructor function for instances to exclude (Mirror)
6813// args[2]: the the maximum number of objects to return
6814static Object* Runtime_DebugReferencedBy(Arguments args) {
6815 ASSERT(args.length() == 3);
6816
6817 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006818 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006819
6820 // Check parameters.
6821 CONVERT_CHECKED(JSObject, target, args[0]);
6822 Object* instance_filter = args[1];
6823 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6824 instance_filter->IsJSObject());
6825 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6826 RUNTIME_ASSERT(max_references >= 0);
6827
6828 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006829 JSObject* arguments_boilerplate =
6830 Top::context()->global_context()->arguments_boilerplate();
6831 JSFunction* arguments_function =
6832 JSFunction::cast(arguments_boilerplate->map()->constructor());
6833
6834 // Get the number of referencing objects.
6835 int count;
6836 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006837 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006838
6839 // Allocate an array to hold the result.
6840 Object* object = Heap::AllocateFixedArray(count);
6841 if (object->IsFailure()) return object;
6842 FixedArray* instances = FixedArray::cast(object);
6843
6844 // Fill the referencing objects.
6845 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006846 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006847
6848 // Return result as JS array.
6849 Object* result =
6850 Heap::AllocateJSObject(
6851 Top::context()->global_context()->array_function());
6852 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6853 return result;
6854}
6855
6856
6857// Helper function used by Runtime_DebugConstructedBy below.
6858static int DebugConstructedBy(JSFunction* constructor, int max_references,
6859 FixedArray* instances, int instances_size) {
6860 AssertNoAllocation no_alloc;
6861
6862 // Iterate the heap.
6863 int count = 0;
6864 HeapIterator iterator;
6865 while (iterator.has_next() &&
6866 (max_references == 0 || count < max_references)) {
6867 // Only look at all JSObjects.
6868 HeapObject* heap_obj = iterator.next();
6869 if (heap_obj->IsJSObject()) {
6870 JSObject* obj = JSObject::cast(heap_obj);
6871 if (obj->map()->constructor() == constructor) {
6872 // Valid reference found add to instance array if supplied an update
6873 // count.
6874 if (instances != NULL && count < instances_size) {
6875 instances->set(count, obj);
6876 }
6877 count++;
6878 }
6879 }
6880 }
6881
6882 // Return the number of referencing objects found.
6883 return count;
6884}
6885
6886
6887// Scan the heap for objects constructed by a specific function.
6888// args[0]: the constructor to find instances of
6889// args[1]: the the maximum number of objects to return
6890static Object* Runtime_DebugConstructedBy(Arguments args) {
6891 ASSERT(args.length() == 2);
6892
6893 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006894 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006895
6896 // Check parameters.
6897 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6898 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6899 RUNTIME_ASSERT(max_references >= 0);
6900
6901 // Get the number of referencing objects.
6902 int count;
6903 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6904
6905 // Allocate an array to hold the result.
6906 Object* object = Heap::AllocateFixedArray(count);
6907 if (object->IsFailure()) return object;
6908 FixedArray* instances = FixedArray::cast(object);
6909
6910 // Fill the referencing objects.
6911 count = DebugConstructedBy(constructor, max_references, instances, count);
6912
6913 // Return result as JS array.
6914 Object* result =
6915 Heap::AllocateJSObject(
6916 Top::context()->global_context()->array_function());
6917 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6918 return result;
6919}
6920
6921
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006922// Find the effective prototype object as returned by __proto__.
6923// args[0]: the object to find the prototype for.
6924static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006925 ASSERT(args.length() == 1);
6926
6927 CONVERT_CHECKED(JSObject, obj, args[0]);
6928
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006929 // Use the __proto__ accessor.
6930 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006931}
6932
6933
6934static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006935 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006936 CPU::DebugBreak();
6937 return Heap::undefined_value();
6938}
6939
6940
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006941static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6942#ifdef DEBUG
6943 HandleScope scope;
6944 ASSERT(args.length() == 1);
6945 // Get the function and make sure it is compiled.
6946 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6947 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6948 return Failure::Exception();
6949 }
6950 func->code()->PrintLn();
6951#endif // DEBUG
6952 return Heap::undefined_value();
6953}
ager@chromium.org9085a012009-05-11 19:22:57 +00006954
6955
6956static Object* Runtime_FunctionGetInferredName(Arguments args) {
6957 NoHandleAllocation ha;
6958 ASSERT(args.length() == 1);
6959
6960 CONVERT_CHECKED(JSFunction, f, args[0]);
6961 return f->shared()->inferred_name();
6962}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006963#endif // ENABLE_DEBUGGER_SUPPORT
6964
6965
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006966// Finds the script object from the script data. NOTE: This operation uses
6967// heap traversal to find the function generated for the source position
6968// for the requested break point. For lazily compiled functions several heap
6969// traversals might be required rendering this operation as a rather slow
6970// operation. However for setting break points which is normally done through
6971// some kind of user interaction the performance is not crucial.
6972static Handle<Object> Runtime_GetScriptFromScriptName(
6973 Handle<String> script_name) {
6974 // Scan the heap for Script objects to find the script with the requested
6975 // script data.
6976 Handle<Script> script;
6977 HeapIterator iterator;
6978 while (script.is_null() && iterator.has_next()) {
6979 HeapObject* obj = iterator.next();
6980 // If a script is found check if it has the script data requested.
6981 if (obj->IsScript()) {
6982 if (Script::cast(obj)->name()->IsString()) {
6983 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6984 script = Handle<Script>(Script::cast(obj));
6985 }
6986 }
6987 }
6988 }
6989
6990 // If no script with the requested script data is found return undefined.
6991 if (script.is_null()) return Factory::undefined_value();
6992
6993 // Return the script found.
6994 return GetScriptWrapper(script);
6995}
6996
6997
6998// Get the script object from script data. NOTE: Regarding performance
6999// see the NOTE for GetScriptFromScriptData.
7000// args[0]: script data for the script to find the source for
7001static Object* Runtime_GetScript(Arguments args) {
7002 HandleScope scope;
7003
7004 ASSERT(args.length() == 1);
7005
7006 CONVERT_CHECKED(String, script_name, args[0]);
7007
7008 // Find the requested script.
7009 Handle<Object> result =
7010 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7011 return *result;
7012}
7013
7014
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007015static Object* Runtime_Abort(Arguments args) {
7016 ASSERT(args.length() == 2);
7017 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7018 Smi::cast(args[1])->value());
7019 Top::PrintStack();
7020 OS::Abort();
7021 UNREACHABLE();
7022 return NULL;
7023}
7024
7025
kasper.lund44510672008-07-25 07:37:58 +00007026#ifdef DEBUG
7027// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7028// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007029static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007030 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007031 HandleScope scope;
7032 Handle<JSArray> result = Factory::NewJSArray(0);
7033 int index = 0;
7034#define ADD_ENTRY(Name, argc) \
7035 { \
7036 HandleScope inner; \
7037 Handle<String> name = \
7038 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7039 Handle<JSArray> pair = Factory::NewJSArray(0); \
7040 SetElement(pair, 0, name); \
7041 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7042 SetElement(result, index++, pair); \
7043 }
7044 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7045#undef ADD_ENTRY
7046 return *result;
7047}
kasper.lund44510672008-07-25 07:37:58 +00007048#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007049
7050
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007051static Object* Runtime_Log(Arguments args) {
7052 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007053 CONVERT_CHECKED(String, format, args[0]);
7054 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007055 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007056 Logger::LogRuntime(chars, elms);
7057 return Heap::undefined_value();
7058}
7059
7060
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007061static Object* Runtime_IS_VAR(Arguments args) {
7062 UNREACHABLE(); // implemented as macro in the parser
7063 return NULL;
7064}
7065
7066
7067// ----------------------------------------------------------------------------
7068// Implementation of Runtime
7069
7070#define F(name, nargs) \
7071 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7072 static_cast<int>(Runtime::k##name) },
7073
7074static Runtime::Function Runtime_functions[] = {
7075 RUNTIME_FUNCTION_LIST(F)
7076 { NULL, NULL, NULL, 0, -1 }
7077};
7078
7079#undef F
7080
7081
7082Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7083 ASSERT(0 <= fid && fid < kNofFunctions);
7084 return &Runtime_functions[fid];
7085}
7086
7087
7088Runtime::Function* Runtime::FunctionForName(const char* name) {
7089 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7090 if (strcmp(f->name, name) == 0) {
7091 return f;
7092 }
7093 }
7094 return NULL;
7095}
7096
7097
7098void Runtime::PerformGC(Object* result) {
7099 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007100 if (failure->IsRetryAfterGC()) {
7101 // Try to do a garbage collection; ignore it if it fails. The C
7102 // entry stub will throw an out-of-memory exception in that case.
7103 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7104 } else {
7105 // Handle last resort GC and make sure to allow future allocations
7106 // to grow the heap without causing GCs (if possible).
7107 Counters::gc_last_resort_from_js.Increment();
7108 Heap::CollectAllGarbage();
7109 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007110}
7111
7112
7113} } // namespace v8::internal