blob: 95776e54af761d0c4350ecfe1aa645441ac0d0b9 [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"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000049
kasperl@chromium.org71affb52009-05-26 05:44:31 +000050namespace v8 {
51namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000052
53
ager@chromium.org3e875802009-06-29 08:26:34 +000054#define RUNTIME_ASSERT(value) \
55 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000056
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
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000100static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
101 StackLimitCheck check;
102 if (check.HasOverflowed()) return Top::StackOverflow();
103
104 Object* result = Heap::CopyJSObject(boilerplate);
105 if (result->IsFailure()) return result;
106 JSObject* copy = JSObject::cast(result);
107
108 // Deep copy local properties.
109 if (copy->HasFastProperties()) {
110 FixedArray* properties = copy->properties();
111 WriteBarrierMode mode = properties->GetWriteBarrierMode();
112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
115 JSObject* jsObject = JSObject::cast(value);
116 result = DeepCopyBoilerplate(jsObject);
117 if (result->IsFailure()) return result;
118 properties->set(i, result, mode);
119 }
120 }
121 mode = copy->GetWriteBarrierMode();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000122 int nof = copy->map()->inobject_properties();
123 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000124 Object* value = copy->InObjectPropertyAt(i);
125 if (value->IsJSObject()) {
126 JSObject* jsObject = JSObject::cast(value);
127 result = DeepCopyBoilerplate(jsObject);
128 if (result->IsFailure()) return result;
129 copy->InObjectPropertyAtPut(i, result, mode);
130 }
131 }
132 } else {
133 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
134 if (result->IsFailure()) return result;
135 FixedArray* names = FixedArray::cast(result);
136 copy->GetLocalPropertyNames(names, 0);
137 for (int i = 0; i < names->length(); i++) {
138 ASSERT(names->get(i)->IsString());
139 String* keyString = String::cast(names->get(i));
140 PropertyAttributes attributes =
141 copy->GetLocalPropertyAttribute(keyString);
142 // Only deep copy fields from the object literal expression.
143 // In particular, don't try to copy the length attribute of
144 // an array.
145 if (attributes != NONE) continue;
146 Object* value = copy->GetProperty(keyString, &attributes);
147 ASSERT(!value->IsFailure());
148 if (value->IsJSObject()) {
149 JSObject* jsObject = JSObject::cast(value);
150 result = DeepCopyBoilerplate(jsObject);
151 if (result->IsFailure()) return result;
152 result = copy->SetProperty(keyString, result, NONE);
153 if (result->IsFailure()) return result;
154 }
155 }
156 }
157
158 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000159 // Pixel elements cannot be created using an object literal.
160 ASSERT(!copy->HasPixelElements());
161 switch (copy->GetElementsKind()) {
162 case JSObject::FAST_ELEMENTS: {
163 FixedArray* elements = FixedArray::cast(copy->elements());
164 WriteBarrierMode mode = elements->GetWriteBarrierMode();
165 for (int i = 0; i < elements->length(); i++) {
166 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000167 if (value->IsJSObject()) {
168 JSObject* jsObject = JSObject::cast(value);
169 result = DeepCopyBoilerplate(jsObject);
170 if (result->IsFailure()) return result;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000171 elements->set(i, result, mode);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000172 }
173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000175 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000176 case JSObject::DICTIONARY_ELEMENTS: {
177 NumberDictionary* element_dictionary = copy->element_dictionary();
178 int capacity = element_dictionary->Capacity();
179 for (int i = 0; i < capacity; i++) {
180 Object* k = element_dictionary->KeyAt(i);
181 if (element_dictionary->IsKey(k)) {
182 Object* value = element_dictionary->ValueAt(i);
183 if (value->IsJSObject()) {
184 JSObject* jsObject = JSObject::cast(value);
185 result = DeepCopyBoilerplate(jsObject);
186 if (result->IsFailure()) return result;
187 element_dictionary->ValueAtPut(i, result);
188 }
189 }
190 }
191 break;
192 }
193 default:
194 UNREACHABLE();
195 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000196 }
197 return copy;
198}
199
200
201static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
202 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
203 return DeepCopyBoilerplate(boilerplate);
204}
205
206
207static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000209 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000210}
211
212
ager@chromium.org236ad962008-09-25 09:45:57 +0000213static Handle<Map> ComputeObjectLiteralMap(
214 Handle<Context> context,
215 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000216 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000217 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 if (FLAG_canonicalize_object_literal_maps) {
219 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000220 int number_of_symbol_keys = 0;
221 while ((number_of_symbol_keys < number_of_properties) &&
222 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
223 number_of_symbol_keys++;
224 }
225 // Based on the number of prefix symbols key we decide whether
226 // to use the map cache in the global context.
227 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000228 if ((number_of_symbol_keys == number_of_properties) &&
229 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000230 // Create the fixed array with the key.
231 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
232 for (int i = 0; i < number_of_symbol_keys; i++) {
233 keys->set(i, constant_properties->get(i*2));
234 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000235 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000236 return Factory::ObjectLiteralMapFromCache(context, keys);
237 }
238 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000239 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000240 return Factory::CopyMap(
241 Handle<Map>(context->object_function()->initial_map()),
242 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000243}
244
245
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000246static Handle<Object> CreateLiteralBoilerplate(
247 Handle<FixedArray> literals,
248 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000249
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000250
251static Handle<Object> CreateObjectLiteralBoilerplate(
252 Handle<FixedArray> literals,
253 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000254 // Get the global context from the literals array. This is the
255 // context in which the function was created and we use the object
256 // function from this context to create the object literal. We do
257 // not use the object function from the current global context
258 // because this might be the object function from another context
259 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000260 Handle<Context> context =
261 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
262
263 bool is_result_from_cache;
264 Handle<Map> map = ComputeObjectLiteralMap(context,
265 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000266 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000267
ager@chromium.org236ad962008-09-25 09:45:57 +0000268 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000269 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000270 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000271 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000272 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000273 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000274 for (int index = 0; index < length; index +=2) {
275 Handle<Object> key(constant_properties->get(index+0));
276 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000277 if (value->IsFixedArray()) {
278 // The value contains the constant_properties of a
279 // simple object literal.
280 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
281 value = CreateLiteralBoilerplate(literals, array);
282 if (value.is_null()) return value;
283 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000284 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000285 uint32_t element_index = 0;
286 if (key->IsSymbol()) {
287 // If key is a symbol it is not an array element.
288 Handle<String> name(String::cast(*key));
289 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000290 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 } else if (Array::IndexFromObject(*key, &element_index)) {
292 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 } else {
295 // Non-uint32 number.
296 ASSERT(key->IsNumber());
297 double num = key->Number();
298 char arr[100];
299 Vector<char> buffer(arr, ARRAY_SIZE(arr));
300 const char* str = DoubleToCString(num, buffer);
301 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000302 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000304 // If setting the property on the boilerplate throws an
305 // exception, the exception is converted to an empty handle in
306 // the handle based operations. In that case, we need to
307 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000308 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309 }
310 }
311
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000312 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000313}
314
315
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000316static Handle<Object> CreateArrayLiteralBoilerplate(
317 Handle<FixedArray> literals,
318 Handle<FixedArray> elements) {
319 // Create the JSArray.
320 Handle<JSFunction> constructor(
321 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
322 Handle<Object> object = Factory::NewJSObject(constructor);
323
324 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
325
326 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
327 for (int i = 0; i < content->length(); i++) {
328 if (content->get(i)->IsFixedArray()) {
329 // The value contains the constant_properties of a
330 // simple object literal.
331 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
332 Handle<Object> result =
333 CreateLiteralBoilerplate(literals, fa);
334 if (result.is_null()) return result;
335 content->set(i, *result);
336 }
337 }
338
339 // Set the elements.
340 Handle<JSArray>::cast(object)->SetContent(*content);
341 return object;
342}
343
344
345static Handle<Object> CreateLiteralBoilerplate(
346 Handle<FixedArray> literals,
347 Handle<FixedArray> array) {
348 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
349 switch (CompileTimeValue::GetType(array)) {
350 case CompileTimeValue::OBJECT_LITERAL:
351 return CreateObjectLiteralBoilerplate(literals, elements);
352 case CompileTimeValue::ARRAY_LITERAL:
353 return CreateArrayLiteralBoilerplate(literals, elements);
354 default:
355 UNREACHABLE();
356 return Handle<Object>::null();
357 }
358}
359
360
361static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
362 HandleScope scope;
363 ASSERT(args.length() == 3);
364 // Copy the arguments.
365 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
366 CONVERT_SMI_CHECKED(literals_index, args[1]);
367 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
368
369 Handle<Object> result =
370 CreateObjectLiteralBoilerplate(literals, constant_properties);
371
372 if (result.is_null()) return Failure::Exception();
373
374 // Update the functions literal and return the boilerplate.
375 literals->set(literals_index, *result);
376
377 return *result;
378}
379
380
381static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000382 // Takes a FixedArray of elements containing the literal elements of
383 // the array literal and produces JSArray with those elements.
384 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000385 // which contains the context from which to get the Array function
386 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000387 HandleScope scope;
388 ASSERT(args.length() == 3);
389 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
390 CONVERT_SMI_CHECKED(literals_index, args[1]);
391 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000392
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000393 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
394 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000395
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000396 // Update the functions literal and return the boilerplate.
397 literals->set(literals_index, *object);
398 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000399}
400
401
ager@chromium.org32912102009-01-16 10:38:43 +0000402static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
403 ASSERT(args.length() == 2);
404 CONVERT_CHECKED(String, key, args[0]);
405 Object* value = args[1];
406 // Create a catch context extension object.
407 JSFunction* constructor =
408 Top::context()->global_context()->context_extension_function();
409 Object* object = Heap::AllocateJSObject(constructor);
410 if (object->IsFailure()) return object;
411 // Assign the exception value to the catch variable and make sure
412 // that the catch variable is DontDelete.
413 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
414 if (value->IsFailure()) return value;
415 return object;
416}
417
418
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000419static Object* Runtime_ClassOf(Arguments args) {
420 NoHandleAllocation ha;
421 ASSERT(args.length() == 1);
422 Object* obj = args[0];
423 if (!obj->IsJSObject()) return Heap::null_value();
424 return JSObject::cast(obj)->class_name();
425}
426
ager@chromium.org7c537e22008-10-16 08:43:32 +0000427
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000428static Object* Runtime_IsInPrototypeChain(Arguments args) {
429 NoHandleAllocation ha;
430 ASSERT(args.length() == 2);
431 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
432 Object* O = args[0];
433 Object* V = args[1];
434 while (true) {
435 Object* prototype = V->GetPrototype();
436 if (prototype->IsNull()) return Heap::false_value();
437 if (O == prototype) return Heap::true_value();
438 V = prototype;
439 }
440}
441
442
ager@chromium.org9085a012009-05-11 19:22:57 +0000443// Inserts an object as the hidden prototype of another object.
444static Object* Runtime_SetHiddenPrototype(Arguments args) {
445 NoHandleAllocation ha;
446 ASSERT(args.length() == 2);
447 CONVERT_CHECKED(JSObject, jsobject, args[0]);
448 CONVERT_CHECKED(JSObject, proto, args[1]);
449
450 // Sanity checks. The old prototype (that we are replacing) could
451 // theoretically be null, but if it is not null then check that we
452 // didn't already install a hidden prototype here.
453 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
454 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
455 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
456
457 // Allocate up front before we start altering state in case we get a GC.
458 Object* map_or_failure = proto->map()->CopyDropTransitions();
459 if (map_or_failure->IsFailure()) return map_or_failure;
460 Map* new_proto_map = Map::cast(map_or_failure);
461
462 map_or_failure = jsobject->map()->CopyDropTransitions();
463 if (map_or_failure->IsFailure()) return map_or_failure;
464 Map* new_map = Map::cast(map_or_failure);
465
466 // Set proto's prototype to be the old prototype of the object.
467 new_proto_map->set_prototype(jsobject->GetPrototype());
468 proto->set_map(new_proto_map);
469 new_proto_map->set_is_hidden_prototype();
470
471 // Set the object's prototype to proto.
472 new_map->set_prototype(proto);
473 jsobject->set_map(new_map);
474
475 return Heap::undefined_value();
476}
477
478
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000479static Object* Runtime_IsConstructCall(Arguments args) {
480 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000481 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000482 JavaScriptFrameIterator it;
483 return Heap::ToBoolean(it.frame()->IsConstructor());
484}
485
486
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000488 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000489 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000490 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
491 CONVERT_ARG_CHECKED(String, pattern, 1);
492 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000493 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
494 if (result.is_null()) return Failure::Exception();
495 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000496}
497
498
499static Object* Runtime_CreateApiFunction(Arguments args) {
500 HandleScope scope;
501 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000502 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000503 return *Factory::CreateApiFunction(data);
504}
505
506
507static Object* Runtime_IsTemplate(Arguments args) {
508 ASSERT(args.length() == 1);
509 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000510 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000511 return Heap::ToBoolean(result);
512}
513
514
515static Object* Runtime_GetTemplateField(Arguments args) {
516 ASSERT(args.length() == 2);
517 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000518 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000519 int index = field->value();
520 int offset = index * kPointerSize + HeapObject::kHeaderSize;
521 InstanceType type = templ->map()->instance_type();
522 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
523 type == OBJECT_TEMPLATE_INFO_TYPE);
524 RUNTIME_ASSERT(offset > 0);
525 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
526 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
527 } else {
528 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
529 }
530 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531}
532
533
ager@chromium.org870a0b62008-11-04 11:43:05 +0000534static Object* Runtime_DisableAccessChecks(Arguments args) {
535 ASSERT(args.length() == 1);
536 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000537 Map* old_map = object->map();
538 bool needs_access_checks = old_map->is_access_check_needed();
539 if (needs_access_checks) {
540 // Copy map so it won't interfere constructor's initial map.
541 Object* new_map = old_map->CopyDropTransitions();
542 if (new_map->IsFailure()) return new_map;
543
544 Map::cast(new_map)->set_is_access_check_needed(false);
545 object->set_map(Map::cast(new_map));
546 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000547 return needs_access_checks ? Heap::true_value() : Heap::false_value();
548}
549
550
551static Object* Runtime_EnableAccessChecks(Arguments args) {
552 ASSERT(args.length() == 1);
553 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000554 Map* old_map = object->map();
555 if (!old_map->is_access_check_needed()) {
556 // Copy map so it won't interfere constructor's initial map.
557 Object* new_map = old_map->CopyDropTransitions();
558 if (new_map->IsFailure()) return new_map;
559
560 Map::cast(new_map)->set_is_access_check_needed(true);
561 object->set_map(Map::cast(new_map));
562 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000563 return Heap::undefined_value();
564}
565
566
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000567static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
568 HandleScope scope;
569 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
570 Handle<Object> args[2] = { type_handle, name };
571 Handle<Object> error =
572 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
573 return Top::Throw(*error);
574}
575
576
577static Object* Runtime_DeclareGlobals(Arguments args) {
578 HandleScope scope;
579 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
580
581 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
582 Handle<Context> context = args.at<Context>(1);
583 bool is_eval = Smi::cast(args[2])->value() == 1;
584
585 // Compute the property attributes. According to ECMA-262, section
586 // 13, page 71, the property must be read-only and
587 // non-deletable. However, neither SpiderMonkey nor KJS creates the
588 // property as read-only, so we don't either.
589 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000591 // Traverse the name/value pairs and set the properties.
592 int length = pairs->length();
593 for (int i = 0; i < length; i += 2) {
594 HandleScope scope;
595 Handle<String> name(String::cast(pairs->get(i)));
596 Handle<Object> value(pairs->get(i + 1));
597
598 // We have to declare a global const property. To capture we only
599 // assign to it when evaluating the assignment for "const x =
600 // <expr>" the initial value is the hole.
601 bool is_const_property = value->IsTheHole();
602
603 if (value->IsUndefined() || is_const_property) {
604 // Lookup the property in the global object, and don't set the
605 // value of the variable if the property is already there.
606 LookupResult lookup;
607 global->Lookup(*name, &lookup);
608 if (lookup.IsProperty()) {
609 // Determine if the property is local by comparing the holder
610 // against the global object. The information will be used to
611 // avoid throwing re-declaration errors when declaring
612 // variables or constants that exist in the prototype chain.
613 bool is_local = (*global == lookup.holder());
614 // Get the property attributes and determine if the property is
615 // read-only.
616 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
617 bool is_read_only = (attributes & READ_ONLY) != 0;
618 if (lookup.type() == INTERCEPTOR) {
619 // If the interceptor says the property is there, we
620 // just return undefined without overwriting the property.
621 // Otherwise, we continue to setting the property.
622 if (attributes != ABSENT) {
623 // Check if the existing property conflicts with regards to const.
624 if (is_local && (is_read_only || is_const_property)) {
625 const char* type = (is_read_only) ? "const" : "var";
626 return ThrowRedeclarationError(type, name);
627 };
628 // The property already exists without conflicting: Go to
629 // the next declaration.
630 continue;
631 }
632 // Fall-through and introduce the absent property by using
633 // SetProperty.
634 } else {
635 if (is_local && (is_read_only || is_const_property)) {
636 const char* type = (is_read_only) ? "const" : "var";
637 return ThrowRedeclarationError(type, name);
638 }
639 // The property already exists without conflicting: Go to
640 // the next declaration.
641 continue;
642 }
643 }
644 } else {
645 // Copy the function and update its context. Use it as value.
646 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
647 Handle<JSFunction> function =
648 Factory::NewFunctionFromBoilerplate(boilerplate, context);
649 value = function;
650 }
651
652 LookupResult lookup;
653 global->LocalLookup(*name, &lookup);
654
655 PropertyAttributes attributes = is_const_property
656 ? static_cast<PropertyAttributes>(base | READ_ONLY)
657 : base;
658
659 if (lookup.IsProperty()) {
660 // There's a local property that we need to overwrite because
661 // we're either declaring a function or there's an interceptor
662 // that claims the property is absent.
663
664 // Check for conflicting re-declarations. We cannot have
665 // conflicting types in case of intercepted properties because
666 // they are absent.
667 if (lookup.type() != INTERCEPTOR &&
668 (lookup.IsReadOnly() || is_const_property)) {
669 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
670 return ThrowRedeclarationError(type, name);
671 }
672 SetProperty(global, name, value, attributes);
673 } else {
674 // If a property with this name does not already exist on the
675 // global object add the property locally. We take special
676 // precautions to always add it as a local property even in case
677 // of callbacks in the prototype chain (this rules out using
678 // SetProperty). Also, we must use the handle-based version to
679 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000680 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000681 }
682 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000683
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000684 return Heap::undefined_value();
685}
686
687
688static Object* Runtime_DeclareContextSlot(Arguments args) {
689 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000690 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691
ager@chromium.org7c537e22008-10-16 08:43:32 +0000692 CONVERT_ARG_CHECKED(Context, context, 0);
693 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000694 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000695 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000697 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000698
699 // Declarations are always done in the function context.
700 context = Handle<Context>(context->fcontext());
701
702 int index;
703 PropertyAttributes attributes;
704 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000705 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000706 context->Lookup(name, flags, &index, &attributes);
707
708 if (attributes != ABSENT) {
709 // The name was declared before; check for conflicting
710 // re-declarations: This is similar to the code in parser.cc in
711 // the AstBuildingParser::Declare function.
712 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
713 // Functions are not read-only.
714 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
715 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
716 return ThrowRedeclarationError(type, name);
717 }
718
719 // Initialize it if necessary.
720 if (*initial_value != NULL) {
721 if (index >= 0) {
722 // The variable or constant context slot should always be in
723 // the function context; not in any outer context nor in the
724 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000725 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726 if (((attributes & READ_ONLY) == 0) ||
727 context->get(index)->IsTheHole()) {
728 context->set(index, *initial_value);
729 }
730 } else {
731 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000732 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733 SetProperty(context_ext, name, initial_value, mode);
734 }
735 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000738 // The property is not in the function context. It needs to be
739 // "declared" in the function context's extension context, or in the
740 // global context.
741 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000742 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000743 // The function context's extension context exists - use it.
744 context_ext = Handle<JSObject>(context->extension());
745 } else {
746 // The function context's extension context does not exists - allocate
747 // it.
748 context_ext = Factory::NewJSObject(Top::context_extension_function());
749 // And store it in the extension slot.
750 context->set_extension(*context_ext);
751 }
752 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000753
ager@chromium.org7c537e22008-10-16 08:43:32 +0000754 // Declare the property by setting it to the initial value if provided,
755 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
756 // constant declarations).
757 ASSERT(!context_ext->HasLocalProperty(*name));
758 Handle<Object> value(Heap::undefined_value());
759 if (*initial_value != NULL) value = initial_value;
760 SetProperty(context_ext, name, value, mode);
761 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
762 }
763
764 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000765}
766
767
768static Object* Runtime_InitializeVarGlobal(Arguments args) {
769 NoHandleAllocation nha;
770
771 // Determine if we need to assign to the variable if it already
772 // exists (based on the number of arguments).
773 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
774 bool assign = args.length() == 2;
775
776 CONVERT_ARG_CHECKED(String, name, 0);
777 GlobalObject* global = Top::context()->global();
778
779 // According to ECMA-262, section 12.2, page 62, the property must
780 // not be deletable.
781 PropertyAttributes attributes = DONT_DELETE;
782
783 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000784 // there, there is a property with this name in the prototype chain.
785 // We follow Safari and Firefox behavior and only set the property
786 // locally if there is an explicit initialization value that we have
787 // to assign to the property. When adding the property we take
788 // special precautions to always add it as a local property even in
789 // case of callbacks in the prototype chain (this rules out using
790 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
791 // this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000792 LookupResult lookup;
793 global->LocalLookup(*name, &lookup);
794 if (!lookup.IsProperty()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000795 if (assign) {
796 return global->IgnoreAttributesAndSetLocalProperty(*name,
797 args[1],
798 attributes);
799 }
800 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000801 }
802
803 // Determine if this is a redeclaration of something read-only.
804 if (lookup.IsReadOnly()) {
805 return ThrowRedeclarationError("const", name);
806 }
807
808 // Determine if this is a redeclaration of an intercepted read-only
809 // property and figure out if the property exists at all.
810 bool found = true;
811 PropertyType type = lookup.type();
812 if (type == INTERCEPTOR) {
813 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
814 if (intercepted == ABSENT) {
815 // The interceptor claims the property isn't there. We need to
816 // make sure to introduce it.
817 found = false;
818 } else if ((intercepted & READ_ONLY) != 0) {
819 // The property is present, but read-only. Since we're trying to
820 // overwrite it with a variable declaration we must throw a
821 // re-declaration error.
822 return ThrowRedeclarationError("const", name);
823 }
824 // Restore global object from context (in case of GC).
825 global = Top::context()->global();
826 }
827
828 if (found && !assign) {
829 // The global property is there and we're not assigning any value
830 // to it. Just return.
831 return Heap::undefined_value();
832 }
833
834 // Assign the value (or undefined) to the property.
835 Object* value = (assign) ? args[1] : Heap::undefined_value();
836 return global->SetProperty(&lookup, *name, value, attributes);
837}
838
839
840static Object* Runtime_InitializeConstGlobal(Arguments args) {
841 // All constants are declared with an initial value. The name
842 // of the constant is the first argument and the initial value
843 // is the second.
844 RUNTIME_ASSERT(args.length() == 2);
845 CONVERT_ARG_CHECKED(String, name, 0);
846 Handle<Object> value = args.at<Object>(1);
847
848 // Get the current global object from top.
849 GlobalObject* global = Top::context()->global();
850
851 // According to ECMA-262, section 12.2, page 62, the property must
852 // not be deletable. Since it's a const, it must be READ_ONLY too.
853 PropertyAttributes attributes =
854 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
855
856 // Lookup the property locally in the global object. If it isn't
857 // there, we add the property and take special precautions to always
858 // add it as a local property even in case of callbacks in the
859 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000860 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000861 LookupResult lookup;
862 global->LocalLookup(*name, &lookup);
863 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000864 return global->IgnoreAttributesAndSetLocalProperty(*name,
865 *value,
866 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000867 }
868
869 // Determine if this is a redeclaration of something not
870 // read-only. In case the result is hidden behind an interceptor we
871 // need to ask it for the property attributes.
872 if (!lookup.IsReadOnly()) {
873 if (lookup.type() != INTERCEPTOR) {
874 return ThrowRedeclarationError("var", name);
875 }
876
877 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
878
879 // Throw re-declaration error if the intercepted property is present
880 // but not read-only.
881 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
882 return ThrowRedeclarationError("var", name);
883 }
884
885 // Restore global object from context (in case of GC) and continue
886 // with setting the value because the property is either absent or
887 // read-only. We also have to do redo the lookup.
888 global = Top::context()->global();
889
890 // BUG 1213579: Handle the case where we have to set a read-only
891 // property through an interceptor and only do it if it's
892 // uninitialized, e.g. the hole. Nirk...
893 global->SetProperty(*name, *value, attributes);
894 return *value;
895 }
896
897 // Set the value, but only we're assigning the initial value to a
898 // constant. For now, we determine this by checking if the
899 // current value is the hole.
900 PropertyType type = lookup.type();
901 if (type == FIELD) {
902 FixedArray* properties = global->properties();
903 int index = lookup.GetFieldIndex();
904 if (properties->get(index)->IsTheHole()) {
905 properties->set(index, *value);
906 }
907 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000908 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
909 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000910 }
911 } else {
912 // Ignore re-initialization of constants that have already been
913 // assigned a function value.
914 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
915 }
916
917 // Use the set value as the result of the operation.
918 return *value;
919}
920
921
922static Object* Runtime_InitializeConstContextSlot(Arguments args) {
923 HandleScope scope;
924 ASSERT(args.length() == 3);
925
926 Handle<Object> value(args[0]);
927 ASSERT(!value->IsTheHole());
928 CONVERT_ARG_CHECKED(Context, context, 1);
929 Handle<String> name(String::cast(args[2]));
930
931 // Initializations are always done in the function context.
932 context = Handle<Context>(context->fcontext());
933
934 int index;
935 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000936 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000937 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000938 context->Lookup(name, flags, &index, &attributes);
939
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000940 // In most situations, the property introduced by the const
941 // declaration should be present in the context extension object.
942 // However, because declaration and initialization are separate, the
943 // property might have been deleted (if it was introduced by eval)
944 // before we reach the initialization point.
945 //
946 // Example:
947 //
948 // function f() { eval("delete x; const x;"); }
949 //
950 // In that case, the initialization behaves like a normal assignment
951 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000952 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000953 // Property was found in a context.
954 if (holder->IsContext()) {
955 // The holder cannot be the function context. If it is, there
956 // should have been a const redeclaration error when declaring
957 // the const property.
958 ASSERT(!holder.is_identical_to(context));
959 if ((attributes & READ_ONLY) == 0) {
960 Handle<Context>::cast(holder)->set(index, *value);
961 }
962 } else {
963 // The holder is an arguments object.
964 ASSERT((attributes & READ_ONLY) == 0);
965 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000966 }
967 return *value;
968 }
969
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000970 // The property could not be found, we introduce it in the global
971 // context.
972 if (attributes == ABSENT) {
973 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
974 SetProperty(global, name, value, NONE);
975 return *value;
976 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000977
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000978 // The property was present in a context extension object.
979 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000980
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000981 if (*context_ext == context->extension()) {
982 // This is the property that was introduced by the const
983 // declaration. Set it if it hasn't been set before. NOTE: We
984 // cannot use GetProperty() to get the current value as it
985 // 'unholes' the value.
986 LookupResult lookup;
987 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
988 ASSERT(lookup.IsProperty()); // the property was declared
989 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
990
991 PropertyType type = lookup.type();
992 if (type == FIELD) {
993 FixedArray* properties = context_ext->properties();
994 int index = lookup.GetFieldIndex();
995 if (properties->get(index)->IsTheHole()) {
996 properties->set(index, *value);
997 }
998 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000999 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1000 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001001 }
1002 } else {
1003 // We should not reach here. Any real, named property should be
1004 // either a field or a dictionary slot.
1005 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001006 }
1007 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001008 // The property was found in a different context extension object.
1009 // Set it if it is not a read-only property.
1010 if ((attributes & READ_ONLY) == 0) {
1011 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1012 // Setting a property might throw an exception. Exceptions
1013 // are converted to empty handles in handle operations. We
1014 // need to convert back to exceptions here.
1015 if (set.is_null()) {
1016 ASSERT(Top::has_pending_exception());
1017 return Failure::Exception();
1018 }
1019 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001020 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001021
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001022 return *value;
1023}
1024
1025
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001026static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1027 Arguments args) {
1028 HandleScope scope;
1029 ASSERT(args.length() == 2);
1030 CONVERT_ARG_CHECKED(JSObject, object, 0);
1031 CONVERT_SMI_CHECKED(properties, args[1]);
1032 if (object->HasFastProperties()) {
1033 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1034 }
1035 return *object;
1036}
1037
1038
1039static Object* Runtime_TransformToFastProperties(Arguments args) {
1040 HandleScope scope;
1041 ASSERT(args.length() == 1);
1042 CONVERT_ARG_CHECKED(JSObject, object, 0);
1043 if (!object->HasFastProperties() && !object->IsGlobalObject()) {
1044 TransformToFastProperties(object, 0);
1045 }
1046 return *object;
1047}
1048
1049
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001050static Object* Runtime_RegExpExec(Arguments args) {
1051 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001052 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001053 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1054 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001055 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001056 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001057 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001058 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001059 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001060 RUNTIME_ASSERT(index >= 0);
1061 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001062 Handle<Object> result = RegExpImpl::Exec(regexp,
1063 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001064 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001065 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001066 if (result.is_null()) return Failure::Exception();
1067 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001068}
1069
1070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001071static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1072 HandleScope scope;
1073 ASSERT(args.length() == 4);
1074 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1075 int index = Smi::cast(args[1])->value();
1076 Handle<String> pattern = args.at<String>(2);
1077 Handle<String> flags = args.at<String>(3);
1078
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001079 // Get the RegExp function from the context in the literals array.
1080 // This is the RegExp function from the context in which the
1081 // function was created. We do not use the RegExp function from the
1082 // current global context because this might be the RegExp function
1083 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001084 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001085 Handle<JSFunction>(
1086 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001087 // Compute the regular expression literal.
1088 bool has_pending_exception;
1089 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001090 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1091 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001092 if (has_pending_exception) {
1093 ASSERT(Top::has_pending_exception());
1094 return Failure::Exception();
1095 }
1096 literals->set(index, *regexp);
1097 return *regexp;
1098}
1099
1100
1101static Object* Runtime_FunctionGetName(Arguments args) {
1102 NoHandleAllocation ha;
1103 ASSERT(args.length() == 1);
1104
1105 CONVERT_CHECKED(JSFunction, f, args[0]);
1106 return f->shared()->name();
1107}
1108
1109
ager@chromium.org236ad962008-09-25 09:45:57 +00001110static Object* Runtime_FunctionSetName(Arguments args) {
1111 NoHandleAllocation ha;
1112 ASSERT(args.length() == 2);
1113
1114 CONVERT_CHECKED(JSFunction, f, args[0]);
1115 CONVERT_CHECKED(String, name, args[1]);
1116 f->shared()->set_name(name);
1117 return Heap::undefined_value();
1118}
1119
1120
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001121static Object* Runtime_FunctionGetScript(Arguments args) {
1122 HandleScope scope;
1123 ASSERT(args.length() == 1);
1124
1125 CONVERT_CHECKED(JSFunction, fun, args[0]);
1126 Handle<Object> script = Handle<Object>(fun->shared()->script());
1127 if (!script->IsScript()) return Heap::undefined_value();
1128
1129 return *GetScriptWrapper(Handle<Script>::cast(script));
1130}
1131
1132
1133static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1134 NoHandleAllocation ha;
1135 ASSERT(args.length() == 1);
1136
1137 CONVERT_CHECKED(JSFunction, f, args[0]);
1138 return f->shared()->GetSourceCode();
1139}
1140
1141
1142static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1143 NoHandleAllocation ha;
1144 ASSERT(args.length() == 1);
1145
1146 CONVERT_CHECKED(JSFunction, fun, args[0]);
1147 int pos = fun->shared()->start_position();
1148 return Smi::FromInt(pos);
1149}
1150
1151
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001152static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1153 ASSERT(args.length() == 2);
1154
1155 CONVERT_CHECKED(JSFunction, fun, args[0]);
1156 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1157
1158 Code* code = fun->code();
1159 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1160
1161 Address pc = code->address() + offset;
1162 return Smi::FromInt(fun->code()->SourcePosition(pc));
1163}
1164
1165
1166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001167static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1168 NoHandleAllocation ha;
1169 ASSERT(args.length() == 2);
1170
1171 CONVERT_CHECKED(JSFunction, fun, args[0]);
1172 CONVERT_CHECKED(String, name, args[1]);
1173 fun->SetInstanceClassName(name);
1174 return Heap::undefined_value();
1175}
1176
1177
1178static Object* Runtime_FunctionSetLength(Arguments args) {
1179 NoHandleAllocation ha;
1180 ASSERT(args.length() == 2);
1181
1182 CONVERT_CHECKED(JSFunction, fun, args[0]);
1183 CONVERT_CHECKED(Smi, length, args[1]);
1184 fun->shared()->set_length(length->value());
1185 return length;
1186}
1187
1188
1189static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001190 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001191 ASSERT(args.length() == 2);
1192
1193 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001194 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1195 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001196 return args[0]; // return TOS
1197}
1198
1199
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001200static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1201 NoHandleAllocation ha;
1202 ASSERT(args.length() == 1);
1203
1204 CONVERT_CHECKED(JSFunction, f, args[0]);
1205 // The function_data field of the shared function info is used exclusively by
1206 // the API.
1207 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1208 : Heap::false_value();
1209}
1210
1211
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001212static Object* Runtime_SetCode(Arguments args) {
1213 HandleScope scope;
1214 ASSERT(args.length() == 2);
1215
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001216 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001217 Handle<Object> code = args.at<Object>(1);
1218
1219 Handle<Context> context(target->context());
1220
1221 if (!code->IsNull()) {
1222 RUNTIME_ASSERT(code->IsJSFunction());
1223 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1224 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1225 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1226 return Failure::Exception();
1227 }
1228 // Set the code, formal parameter count, and the length of the target
1229 // function.
1230 target->set_code(fun->code());
1231 target->shared()->set_length(fun->shared()->length());
1232 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001233 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001234 // Set the source code of the target function to undefined.
1235 // SetCode is only used for built-in constructors like String,
1236 // Array, and Object, and some web code
1237 // doesn't like seeing source code for constructors.
1238 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001239 // Clear the optimization hints related to the compiled code as these are no
1240 // longer valid when the code is overwritten.
1241 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001242 context = Handle<Context>(fun->context());
1243
1244 // Make sure we get a fresh copy of the literal vector to avoid
1245 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001246 int number_of_literals = fun->NumberOfLiterals();
1247 Handle<FixedArray> literals =
1248 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001250 // Insert the object, regexp and array functions in the literals
1251 // array prefix. These are the functions that will be used when
1252 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001253 literals->set(JSFunction::kLiteralGlobalContextIndex,
1254 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001255 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001256 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001257 }
1258
1259 target->set_context(*context);
1260 return *target;
1261}
1262
1263
1264static Object* CharCodeAt(String* subject, Object* index) {
1265 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001266 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001267 // Flatten the string. If someone wants to get a char at an index
1268 // in a cons string, it is likely that more indices will be
1269 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001270 subject->TryFlattenIfNotFlat();
1271 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001272 return Heap::nan_value();
1273 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001274 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001275}
1276
1277
1278static Object* Runtime_StringCharCodeAt(Arguments args) {
1279 NoHandleAllocation ha;
1280 ASSERT(args.length() == 2);
1281
1282 CONVERT_CHECKED(String, subject, args[0]);
1283 Object* index = args[1];
1284 return CharCodeAt(subject, index);
1285}
1286
1287
1288static Object* Runtime_CharFromCode(Arguments args) {
1289 NoHandleAllocation ha;
1290 ASSERT(args.length() == 1);
1291 uint32_t code;
1292 if (Array::IndexFromObject(args[0], &code)) {
1293 if (code <= 0xffff) {
1294 return Heap::LookupSingleCharacterStringFromCode(code);
1295 }
1296 }
1297 return Heap::empty_string();
1298}
1299
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001300// Forward declarations.
1301static const int kStringBuilderConcatHelperLengthBits = 11;
1302static const int kStringBuilderConcatHelperPositionBits = 19;
1303
1304template <typename schar>
1305static inline void StringBuilderConcatHelper(String*,
1306 schar*,
1307 FixedArray*,
1308 int);
1309
1310typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1311typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1312
1313class ReplacementStringBuilder {
1314 public:
1315 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1316 : subject_(subject),
1317 parts_(Factory::NewFixedArray(estimated_part_count)),
1318 part_count_(0),
1319 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001320 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001321 // Require a non-zero initial size. Ensures that doubling the size to
1322 // extend the array will work.
1323 ASSERT(estimated_part_count > 0);
1324 }
1325
1326 void EnsureCapacity(int elements) {
1327 int length = parts_->length();
1328 int required_length = part_count_ + elements;
1329 if (length < required_length) {
1330 int new_length = length;
1331 do {
1332 new_length *= 2;
1333 } while (new_length < required_length);
1334 Handle<FixedArray> extended_array =
1335 Factory::NewFixedArray(new_length);
1336 parts_->CopyTo(0, *extended_array, 0, part_count_);
1337 parts_ = extended_array;
1338 }
1339 }
1340
1341 void AddSubjectSlice(int from, int to) {
1342 ASSERT(from >= 0);
1343 int length = to - from;
1344 ASSERT(length > 0);
1345 // Can we encode the slice in 11 bits for length and 19 bits for
1346 // start position - as used by StringBuilderConcatHelper?
1347 if (StringBuilderSubstringLength::is_valid(length) &&
1348 StringBuilderSubstringPosition::is_valid(from)) {
1349 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1350 StringBuilderSubstringPosition::encode(from);
1351 AddElement(Smi::FromInt(encoded_slice));
1352 } else {
1353 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1354 AddElement(*slice);
1355 }
1356 IncrementCharacterCount(length);
1357 }
1358
1359
1360 void AddString(Handle<String> string) {
1361 int length = string->length();
1362 ASSERT(length > 0);
1363 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001364 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001365 is_ascii_ = false;
1366 }
1367 IncrementCharacterCount(length);
1368 }
1369
1370
1371 Handle<String> ToString() {
1372 if (part_count_ == 0) {
1373 return Factory::empty_string();
1374 }
1375
1376 Handle<String> joined_string;
1377 if (is_ascii_) {
1378 joined_string = NewRawAsciiString(character_count_);
1379 AssertNoAllocation no_alloc;
1380 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1381 char* char_buffer = seq->GetChars();
1382 StringBuilderConcatHelper(*subject_,
1383 char_buffer,
1384 *parts_,
1385 part_count_);
1386 } else {
1387 // Non-ASCII.
1388 joined_string = NewRawTwoByteString(character_count_);
1389 AssertNoAllocation no_alloc;
1390 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1391 uc16* char_buffer = seq->GetChars();
1392 StringBuilderConcatHelper(*subject_,
1393 char_buffer,
1394 *parts_,
1395 part_count_);
1396 }
1397 return joined_string;
1398 }
1399
1400
1401 void IncrementCharacterCount(int by) {
1402 if (character_count_ > Smi::kMaxValue - by) {
1403 V8::FatalProcessOutOfMemory("String.replace result too large.");
1404 }
1405 character_count_ += by;
1406 }
1407
1408 private:
1409
1410 Handle<String> NewRawAsciiString(int size) {
1411 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1412 }
1413
1414
1415 Handle<String> NewRawTwoByteString(int size) {
1416 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1417 }
1418
1419
1420 void AddElement(Object* element) {
1421 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001422 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001423 parts_->set(part_count_, element);
1424 part_count_++;
1425 }
1426
1427 Handle<String> subject_;
1428 Handle<FixedArray> parts_;
1429 int part_count_;
1430 int character_count_;
1431 bool is_ascii_;
1432};
1433
1434
1435class CompiledReplacement {
1436 public:
1437 CompiledReplacement()
1438 : parts_(1), replacement_substrings_(0) {}
1439
1440 void Compile(Handle<String> replacement,
1441 int capture_count,
1442 int subject_length);
1443
1444 void Apply(ReplacementStringBuilder* builder,
1445 int match_from,
1446 int match_to,
1447 Handle<JSArray> last_match_info);
1448
1449 // Number of distinct parts of the replacement pattern.
1450 int parts() {
1451 return parts_.length();
1452 }
1453 private:
1454 enum PartType {
1455 SUBJECT_PREFIX = 1,
1456 SUBJECT_SUFFIX,
1457 SUBJECT_CAPTURE,
1458 REPLACEMENT_SUBSTRING,
1459 REPLACEMENT_STRING,
1460
1461 NUMBER_OF_PART_TYPES
1462 };
1463
1464 struct ReplacementPart {
1465 static inline ReplacementPart SubjectMatch() {
1466 return ReplacementPart(SUBJECT_CAPTURE, 0);
1467 }
1468 static inline ReplacementPart SubjectCapture(int capture_index) {
1469 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1470 }
1471 static inline ReplacementPart SubjectPrefix() {
1472 return ReplacementPart(SUBJECT_PREFIX, 0);
1473 }
1474 static inline ReplacementPart SubjectSuffix(int subject_length) {
1475 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1476 }
1477 static inline ReplacementPart ReplacementString() {
1478 return ReplacementPart(REPLACEMENT_STRING, 0);
1479 }
1480 static inline ReplacementPart ReplacementSubString(int from, int to) {
1481 ASSERT(from >= 0);
1482 ASSERT(to > from);
1483 return ReplacementPart(-from, to);
1484 }
1485
1486 // If tag <= 0 then it is the negation of a start index of a substring of
1487 // the replacement pattern, otherwise it's a value from PartType.
1488 ReplacementPart(int tag, int data)
1489 : tag(tag), data(data) {
1490 // Must be non-positive or a PartType value.
1491 ASSERT(tag < NUMBER_OF_PART_TYPES);
1492 }
1493 // Either a value of PartType or a non-positive number that is
1494 // the negation of an index into the replacement string.
1495 int tag;
1496 // The data value's interpretation depends on the value of tag:
1497 // tag == SUBJECT_PREFIX ||
1498 // tag == SUBJECT_SUFFIX: data is unused.
1499 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1500 // tag == REPLACEMENT_SUBSTRING ||
1501 // tag == REPLACEMENT_STRING: data is index into array of substrings
1502 // of the replacement string.
1503 // tag <= 0: Temporary representation of the substring of the replacement
1504 // string ranging over -tag .. data.
1505 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1506 // substring objects.
1507 int data;
1508 };
1509
1510 template<typename Char>
1511 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1512 Vector<Char> characters,
1513 int capture_count,
1514 int subject_length) {
1515 int length = characters.length();
1516 int last = 0;
1517 for (int i = 0; i < length; i++) {
1518 Char c = characters[i];
1519 if (c == '$') {
1520 int next_index = i + 1;
1521 if (next_index == length) { // No next character!
1522 break;
1523 }
1524 Char c2 = characters[next_index];
1525 switch (c2) {
1526 case '$':
1527 if (i > last) {
1528 // There is a substring before. Include the first "$".
1529 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1530 last = next_index + 1; // Continue after the second "$".
1531 } else {
1532 // Let the next substring start with the second "$".
1533 last = next_index;
1534 }
1535 i = next_index;
1536 break;
1537 case '`':
1538 if (i > last) {
1539 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1540 }
1541 parts->Add(ReplacementPart::SubjectPrefix());
1542 i = next_index;
1543 last = i + 1;
1544 break;
1545 case '\'':
1546 if (i > last) {
1547 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1548 }
1549 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1550 i = next_index;
1551 last = i + 1;
1552 break;
1553 case '&':
1554 if (i > last) {
1555 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1556 }
1557 parts->Add(ReplacementPart::SubjectMatch());
1558 i = next_index;
1559 last = i + 1;
1560 break;
1561 case '0':
1562 case '1':
1563 case '2':
1564 case '3':
1565 case '4':
1566 case '5':
1567 case '6':
1568 case '7':
1569 case '8':
1570 case '9': {
1571 int capture_ref = c2 - '0';
1572 if (capture_ref > capture_count) {
1573 i = next_index;
1574 continue;
1575 }
1576 int second_digit_index = next_index + 1;
1577 if (second_digit_index < length) {
1578 // Peek ahead to see if we have two digits.
1579 Char c3 = characters[second_digit_index];
1580 if ('0' <= c3 && c3 <= '9') { // Double digits.
1581 int double_digit_ref = capture_ref * 10 + c3 - '0';
1582 if (double_digit_ref <= capture_count) {
1583 next_index = second_digit_index;
1584 capture_ref = double_digit_ref;
1585 }
1586 }
1587 }
1588 if (capture_ref > 0) {
1589 if (i > last) {
1590 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1591 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001592 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001593 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1594 last = next_index + 1;
1595 }
1596 i = next_index;
1597 break;
1598 }
1599 default:
1600 i = next_index;
1601 break;
1602 }
1603 }
1604 }
1605 if (length > last) {
1606 if (last == 0) {
1607 parts->Add(ReplacementPart::ReplacementString());
1608 } else {
1609 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1610 }
1611 }
1612 }
1613
1614 ZoneList<ReplacementPart> parts_;
1615 ZoneList<Handle<String> > replacement_substrings_;
1616};
1617
1618
1619void CompiledReplacement::Compile(Handle<String> replacement,
1620 int capture_count,
1621 int subject_length) {
1622 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001623 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001624 AssertNoAllocation no_alloc;
1625 ParseReplacementPattern(&parts_,
1626 replacement->ToAsciiVector(),
1627 capture_count,
1628 subject_length);
1629 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001630 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001631 AssertNoAllocation no_alloc;
1632
1633 ParseReplacementPattern(&parts_,
1634 replacement->ToUC16Vector(),
1635 capture_count,
1636 subject_length);
1637 }
1638 // Find substrings of replacement string and create them as String objects..
1639 int substring_index = 0;
1640 for (int i = 0, n = parts_.length(); i < n; i++) {
1641 int tag = parts_[i].tag;
1642 if (tag <= 0) { // A replacement string slice.
1643 int from = -tag;
1644 int to = parts_[i].data;
1645 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1646 from,
1647 to));
1648 parts_[i].tag = REPLACEMENT_SUBSTRING;
1649 parts_[i].data = substring_index;
1650 substring_index++;
1651 } else if (tag == REPLACEMENT_STRING) {
1652 replacement_substrings_.Add(replacement);
1653 parts_[i].data = substring_index;
1654 substring_index++;
1655 }
1656 }
1657}
1658
1659
1660void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1661 int match_from,
1662 int match_to,
1663 Handle<JSArray> last_match_info) {
1664 for (int i = 0, n = parts_.length(); i < n; i++) {
1665 ReplacementPart part = parts_[i];
1666 switch (part.tag) {
1667 case SUBJECT_PREFIX:
1668 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1669 break;
1670 case SUBJECT_SUFFIX: {
1671 int subject_length = part.data;
1672 if (match_to < subject_length) {
1673 builder->AddSubjectSlice(match_to, subject_length);
1674 }
1675 break;
1676 }
1677 case SUBJECT_CAPTURE: {
1678 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001679 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001680 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1681 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1682 if (from >= 0 && to > from) {
1683 builder->AddSubjectSlice(from, to);
1684 }
1685 break;
1686 }
1687 case REPLACEMENT_SUBSTRING:
1688 case REPLACEMENT_STRING:
1689 builder->AddString(replacement_substrings_[part.data]);
1690 break;
1691 default:
1692 UNREACHABLE();
1693 }
1694 }
1695}
1696
1697
1698
1699static Object* StringReplaceRegExpWithString(String* subject,
1700 JSRegExp* regexp,
1701 String* replacement,
1702 JSArray* last_match_info) {
1703 ASSERT(subject->IsFlat());
1704 ASSERT(replacement->IsFlat());
1705
1706 HandleScope handles;
1707
1708 int length = subject->length();
1709 Handle<String> subject_handle(subject);
1710 Handle<JSRegExp> regexp_handle(regexp);
1711 Handle<String> replacement_handle(replacement);
1712 Handle<JSArray> last_match_info_handle(last_match_info);
1713 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1714 subject_handle,
1715 0,
1716 last_match_info_handle);
1717 if (match.is_null()) {
1718 return Failure::Exception();
1719 }
1720 if (match->IsNull()) {
1721 return *subject_handle;
1722 }
1723
1724 int capture_count = regexp_handle->CaptureCount();
1725
1726 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001727 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001728 CompiledReplacement compiled_replacement;
1729 compiled_replacement.Compile(replacement_handle,
1730 capture_count,
1731 length);
1732
1733 bool is_global = regexp_handle->GetFlags().is_global();
1734
1735 // Guessing the number of parts that the final result string is built
1736 // from. Global regexps can match any number of times, so we guess
1737 // conservatively.
1738 int expected_parts =
1739 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1740 ReplacementStringBuilder builder(subject_handle, expected_parts);
1741
1742 // Index of end of last match.
1743 int prev = 0;
1744
1745 // Number of parts added by compiled replacement plus preceeding string
1746 // and possibly suffix after last match.
1747 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1748 bool matched = true;
1749 do {
1750 ASSERT(last_match_info_handle->HasFastElements());
1751 // Increase the capacity of the builder before entering local handle-scope,
1752 // so its internal buffer can safely allocate a new handle if it grows.
1753 builder.EnsureCapacity(parts_added_per_loop);
1754
1755 HandleScope loop_scope;
1756 int start, end;
1757 {
1758 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001759 FixedArray* match_info_array =
1760 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001761
1762 ASSERT_EQ(capture_count * 2 + 2,
1763 RegExpImpl::GetLastCaptureCount(match_info_array));
1764 start = RegExpImpl::GetCapture(match_info_array, 0);
1765 end = RegExpImpl::GetCapture(match_info_array, 1);
1766 }
1767
1768 if (prev < start) {
1769 builder.AddSubjectSlice(prev, start);
1770 }
1771 compiled_replacement.Apply(&builder,
1772 start,
1773 end,
1774 last_match_info_handle);
1775 prev = end;
1776
1777 // Only continue checking for global regexps.
1778 if (!is_global) break;
1779
1780 // Continue from where the match ended, unless it was an empty match.
1781 int next = end;
1782 if (start == end) {
1783 next = end + 1;
1784 if (next > length) break;
1785 }
1786
1787 match = RegExpImpl::Exec(regexp_handle,
1788 subject_handle,
1789 next,
1790 last_match_info_handle);
1791 if (match.is_null()) {
1792 return Failure::Exception();
1793 }
1794 matched = !match->IsNull();
1795 } while (matched);
1796
1797 if (prev < length) {
1798 builder.AddSubjectSlice(prev, length);
1799 }
1800
1801 return *(builder.ToString());
1802}
1803
1804
1805static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1806 ASSERT(args.length() == 4);
1807
1808 CONVERT_CHECKED(String, subject, args[0]);
1809 if (!subject->IsFlat()) {
1810 Object* flat_subject = subject->TryFlatten();
1811 if (flat_subject->IsFailure()) {
1812 return flat_subject;
1813 }
1814 subject = String::cast(flat_subject);
1815 }
1816
1817 CONVERT_CHECKED(String, replacement, args[2]);
1818 if (!replacement->IsFlat()) {
1819 Object* flat_replacement = replacement->TryFlatten();
1820 if (flat_replacement->IsFailure()) {
1821 return flat_replacement;
1822 }
1823 replacement = String::cast(flat_replacement);
1824 }
1825
1826 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1827 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1828
1829 ASSERT(last_match_info->HasFastElements());
1830
1831 return StringReplaceRegExpWithString(subject,
1832 regexp,
1833 replacement,
1834 last_match_info);
1835}
1836
1837
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001838
ager@chromium.org7c537e22008-10-16 08:43:32 +00001839// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1840// limit, we can fix the size of tables.
1841static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001842// Reduce alphabet to this size.
1843static const int kBMAlphabetSize = 0x100;
1844// For patterns below this length, the skip length of Boyer-Moore is too short
1845// to compensate for the algorithmic overhead compared to simple brute force.
1846static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001847
ager@chromium.org7c537e22008-10-16 08:43:32 +00001848// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1849// shift. Only allows the last kBMMaxShift characters of the needle
1850// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001851class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001852 public:
1853 BMGoodSuffixBuffers() {}
1854 inline void init(int needle_length) {
1855 ASSERT(needle_length > 1);
1856 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1857 int len = needle_length - start;
1858 biased_suffixes_ = suffixes_ - start;
1859 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1860 for (int i = 0; i <= len; i++) {
1861 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001862 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001863 }
1864 inline int& suffix(int index) {
1865 ASSERT(biased_suffixes_ + index >= suffixes_);
1866 return biased_suffixes_[index];
1867 }
1868 inline int& shift(int index) {
1869 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1870 return biased_good_suffix_shift_[index];
1871 }
1872 private:
1873 int suffixes_[kBMMaxShift + 1];
1874 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001875 int* biased_suffixes_;
1876 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001877 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1878};
1879
1880// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001881static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001882static BMGoodSuffixBuffers bmgs_buffers;
1883
1884// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001885template <typename pchar>
1886static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1887 int start) {
1888 // Run forwards to populate bad_char_table, so that *last* instance
1889 // of character equivalence class is the one registered.
1890 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001891 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1892 : kBMAlphabetSize;
1893 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001894 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001895 } else {
1896 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001897 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001898 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001899 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001900 for (int i = start; i < pattern.length() - 1; i++) {
1901 pchar c = pattern[i];
1902 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001903 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001904 }
1905}
1906
1907template <typename pchar>
1908static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001909 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001910 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001911 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001912 // Compute Good Suffix tables.
1913 bmgs_buffers.init(m);
1914
1915 bmgs_buffers.shift(m-1) = 1;
1916 bmgs_buffers.suffix(m) = m + 1;
1917 pchar last_char = pattern[m - 1];
1918 int suffix = m + 1;
1919 for (int i = m; i > start;) {
1920 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1921 if (bmgs_buffers.shift(suffix) == len) {
1922 bmgs_buffers.shift(suffix) = suffix - i;
1923 }
1924 suffix = bmgs_buffers.suffix(suffix);
1925 }
1926 i--;
1927 suffix--;
1928 bmgs_buffers.suffix(i) = suffix;
1929 if (suffix == m) {
1930 // No suffix to extend, so we check against last_char only.
1931 while (i > start && pattern[i - 1] != last_char) {
1932 if (bmgs_buffers.shift(m) == len) {
1933 bmgs_buffers.shift(m) = m - i;
1934 }
1935 i--;
1936 bmgs_buffers.suffix(i) = m;
1937 }
1938 if (i > start) {
1939 i--;
1940 suffix--;
1941 bmgs_buffers.suffix(i) = suffix;
1942 }
1943 }
1944 }
1945 if (suffix < m) {
1946 for (int i = start; i <= m; i++) {
1947 if (bmgs_buffers.shift(i) == len) {
1948 bmgs_buffers.shift(i) = suffix - start;
1949 }
1950 if (i == suffix) {
1951 suffix = bmgs_buffers.suffix(suffix);
1952 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001953 }
1954 }
1955}
1956
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001957template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001958static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001959 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001960 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001961 }
1962 if (sizeof(pchar) == 1) {
1963 if (char_code > String::kMaxAsciiCharCode) {
1964 return -1;
1965 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001966 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001967 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001968 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001969}
1970
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001971// Restricted simplified Boyer-Moore string matching.
1972// Uses only the bad-shift table of Boyer-Moore and only uses it
1973// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001974template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001975static int BoyerMooreHorspool(Vector<const schar> subject,
1976 Vector<const pchar> pattern,
1977 int start_index,
1978 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001979 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001980 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001981 // Only preprocess at most kBMMaxShift last characters of pattern.
1982 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001983
ager@chromium.org7c537e22008-10-16 08:43:32 +00001984 BoyerMoorePopulateBadCharTable(pattern, start);
1985
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001986 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001987 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001988 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001989 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001990 // Perform search
1991 for (idx = start_index; idx <= n - m;) {
1992 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001993 int c;
1994 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001995 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001996 int shift = j - bc_occ;
1997 idx += shift;
1998 badness += 1 - shift; // at most zero, so badness cannot increase.
1999 if (idx > n - m) {
2000 *complete = true;
2001 return -1;
2002 }
2003 }
2004 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002005 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002006 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002007 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002008 return idx;
2009 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002010 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002011 // Badness increases by the number of characters we have
2012 // checked, and decreases by the number of characters we
2013 // can skip by shifting. It's a measure of how we are doing
2014 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002015 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002016 if (badness > 0) {
2017 *complete = false;
2018 return idx;
2019 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002020 }
2021 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002022 *complete = true;
2023 return -1;
2024}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002025
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002026
2027template <typename schar, typename pchar>
2028static int BoyerMooreIndexOf(Vector<const schar> subject,
2029 Vector<const pchar> pattern,
2030 int idx) {
2031 int n = subject.length();
2032 int m = pattern.length();
2033 // Only preprocess at most kBMMaxShift last characters of pattern.
2034 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2035
2036 // Build the Good Suffix table and continue searching.
2037 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2038 pchar last_char = pattern[m - 1];
2039 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002040 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002041 int j = m - 1;
2042 schar c;
2043 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002044 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002045 idx += shift;
2046 if (idx > n - m) {
2047 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002048 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002049 }
2050 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2051 if (j < 0) {
2052 return idx;
2053 } else if (j < start) {
2054 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002055 // Fall back on BMH shift.
2056 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002057 } else {
2058 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002059 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002060 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002061 if (gs_shift > shift) {
2062 shift = gs_shift;
2063 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002064 idx += shift;
2065 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002066 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002067
2068 return -1;
2069}
2070
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002071
2072template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002073static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002074 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002075 int start_index) {
2076 for (int i = start_index, n = string.length(); i < n; i++) {
2077 if (pattern_char == string[i]) {
2078 return i;
2079 }
2080 }
2081 return -1;
2082}
2083
2084// Trivial string search for shorter strings.
2085// On return, if "complete" is set to true, the return value is the
2086// final result of searching for the patter in the subject.
2087// If "complete" is set to false, the return value is the index where
2088// further checking should start, i.e., it's guaranteed that the pattern
2089// does not occur at a position prior to the returned index.
2090template <typename pchar, typename schar>
2091static int SimpleIndexOf(Vector<const schar> subject,
2092 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002093 int idx,
2094 bool* complete) {
2095 // Badness is a count of how much work we have done. When we have
2096 // done enough work we decide it's probably worth switching to a better
2097 // algorithm.
2098 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002099 // We know our pattern is at least 2 characters, we cache the first so
2100 // the common case of the first character not matching is faster.
2101 pchar pattern_first_char = pattern[0];
2102
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002103 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2104 badness++;
2105 if (badness > 0) {
2106 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002107 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002108 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002109 if (subject[i] != pattern_first_char) continue;
2110 int j = 1;
2111 do {
2112 if (pattern[j] != subject[i+j]) {
2113 break;
2114 }
2115 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002116 } while (j < pattern.length());
2117 if (j == pattern.length()) {
2118 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002119 return i;
2120 }
2121 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002122 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002123 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002124 return -1;
2125}
2126
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002127// Simple indexOf that never bails out. For short patterns only.
2128template <typename pchar, typename schar>
2129static int SimpleIndexOf(Vector<const schar> subject,
2130 Vector<const pchar> pattern,
2131 int idx) {
2132 pchar pattern_first_char = pattern[0];
2133 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2134 if (subject[i] != pattern_first_char) continue;
2135 int j = 1;
2136 do {
2137 if (pattern[j] != subject[i+j]) {
2138 break;
2139 }
2140 j++;
2141 } while (j < pattern.length());
2142 if (j == pattern.length()) {
2143 return i;
2144 }
2145 }
2146 return -1;
2147}
2148
2149
2150// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002151template <typename schar, typename pchar>
2152static int StringMatchStrategy(Vector<const schar> sub,
2153 Vector<const pchar> pat,
2154 int start_index) {
2155 ASSERT(pat.length() > 1);
2156
2157 // We have an ASCII haystack and a non-ASCII needle. Check if there
2158 // really is a non-ASCII character in the needle and bail out if there
2159 // is.
2160 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2161 for (int i = 0; i < pat.length(); i++) {
2162 uc16 c = pat[i];
2163 if (c > String::kMaxAsciiCharCode) {
2164 return -1;
2165 }
2166 }
2167 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002168 if (pat.length() < kBMMinPatternLength) {
2169 // We don't believe fancy searching can ever be more efficient.
2170 // The max shift of Boyer-Moore on a pattern of this length does
2171 // not compensate for the overhead.
2172 return SimpleIndexOf(sub, pat, start_index);
2173 }
2174 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002175 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002176 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2177 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002178 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002179 if (complete) return idx;
2180 return BoyerMooreIndexOf(sub, pat, idx);
2181}
2182
2183// Perform string match of pattern on subject, starting at start index.
2184// Caller must ensure that 0 <= start_index <= sub->length(),
2185// and should check that pat->length() + start_index <= sub->length()
2186int Runtime::StringMatch(Handle<String> sub,
2187 Handle<String> pat,
2188 int start_index) {
2189 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002190 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002191
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002192 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002193 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002194
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002195 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002196 if (start_index + pattern_length > subject_length) return -1;
2197
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002198 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002199 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002200 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002201 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002202 // character patterns linear search is necessary, so any smart
2203 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002204 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002205 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002206 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002207 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002208 if (pchar > String::kMaxAsciiCharCode) {
2209 return -1;
2210 }
2211 Vector<const char> ascii_vector =
2212 sub->ToAsciiVector().SubVector(start_index, subject_length);
2213 const void* pos = memchr(ascii_vector.start(),
2214 static_cast<const char>(pchar),
2215 static_cast<size_t>(ascii_vector.length()));
2216 if (pos == NULL) {
2217 return -1;
2218 }
2219 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2220 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002221 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002222 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002223 }
2224
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002225 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002226 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002227 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002228
ager@chromium.org7c537e22008-10-16 08:43:32 +00002229 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2230 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002231 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002232 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002233 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002234 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002235 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002236 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002237 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002238 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002239 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002240 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002241 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002242 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002243}
2244
2245
2246static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002247 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002248 ASSERT(args.length() == 3);
2249
ager@chromium.org7c537e22008-10-16 08:43:32 +00002250 CONVERT_ARG_CHECKED(String, sub, 0);
2251 CONVERT_ARG_CHECKED(String, pat, 1);
2252
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002253 Object* index = args[2];
2254 uint32_t start_index;
2255 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2256
ager@chromium.org870a0b62008-11-04 11:43:05 +00002257 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002258 int position = Runtime::StringMatch(sub, pat, start_index);
2259 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002260}
2261
2262
2263static Object* Runtime_StringLastIndexOf(Arguments args) {
2264 NoHandleAllocation ha;
2265 ASSERT(args.length() == 3);
2266
2267 CONVERT_CHECKED(String, sub, args[0]);
2268 CONVERT_CHECKED(String, pat, args[1]);
2269 Object* index = args[2];
2270
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002271 sub->TryFlattenIfNotFlat();
2272 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002273
2274 uint32_t start_index;
2275 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2276
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002277 uint32_t pattern_length = pat->length();
2278 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002279
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002280 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002281 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002282 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002283
2284 for (int i = start_index; i >= 0; i--) {
2285 bool found = true;
2286 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002287 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002288 found = false;
2289 break;
2290 }
2291 }
2292 if (found) return Smi::FromInt(i);
2293 }
2294
2295 return Smi::FromInt(-1);
2296}
2297
2298
2299static Object* Runtime_StringLocaleCompare(Arguments args) {
2300 NoHandleAllocation ha;
2301 ASSERT(args.length() == 2);
2302
2303 CONVERT_CHECKED(String, str1, args[0]);
2304 CONVERT_CHECKED(String, str2, args[1]);
2305
2306 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002307 int str1_length = str1->length();
2308 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002309
2310 // Decide trivial cases without flattening.
2311 if (str1_length == 0) {
2312 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2313 return Smi::FromInt(-str2_length);
2314 } else {
2315 if (str2_length == 0) return Smi::FromInt(str1_length);
2316 }
2317
2318 int end = str1_length < str2_length ? str1_length : str2_length;
2319
2320 // No need to flatten if we are going to find the answer on the first
2321 // character. At this point we know there is at least one character
2322 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002323 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002324 if (d != 0) return Smi::FromInt(d);
2325
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002326 str1->TryFlattenIfNotFlat();
2327 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002328
2329 static StringInputBuffer buf1;
2330 static StringInputBuffer buf2;
2331
2332 buf1.Reset(str1);
2333 buf2.Reset(str2);
2334
2335 for (int i = 0; i < end; i++) {
2336 uint16_t char1 = buf1.GetNext();
2337 uint16_t char2 = buf2.GetNext();
2338 if (char1 != char2) return Smi::FromInt(char1 - char2);
2339 }
2340
2341 return Smi::FromInt(str1_length - str2_length);
2342}
2343
2344
2345static Object* Runtime_StringSlice(Arguments args) {
2346 NoHandleAllocation ha;
2347 ASSERT(args.length() == 3);
2348
2349 CONVERT_CHECKED(String, value, args[0]);
2350 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2351 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2352
2353 int start = FastD2I(from_number);
2354 int end = FastD2I(to_number);
2355
2356 RUNTIME_ASSERT(end >= start);
2357 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002358 RUNTIME_ASSERT(end <= value->length());
2359 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002360}
2361
2362
ager@chromium.org41826e72009-03-30 13:30:57 +00002363static Object* Runtime_StringMatch(Arguments args) {
2364 ASSERT_EQ(3, args.length());
2365
2366 CONVERT_ARG_CHECKED(String, subject, 0);
2367 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2368 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2369 HandleScope handles;
2370
2371 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2372
2373 if (match.is_null()) {
2374 return Failure::Exception();
2375 }
2376 if (match->IsNull()) {
2377 return Heap::null_value();
2378 }
2379 int length = subject->length();
2380
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002381 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002382 ZoneList<int> offsets(8);
2383 do {
2384 int start;
2385 int end;
2386 {
2387 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002388 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002389 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2390 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2391 }
2392 offsets.Add(start);
2393 offsets.Add(end);
2394 int index = start < end ? end : end + 1;
2395 if (index > length) break;
2396 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2397 if (match.is_null()) {
2398 return Failure::Exception();
2399 }
2400 } while (!match->IsNull());
2401 int matches = offsets.length() / 2;
2402 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2403 for (int i = 0; i < matches ; i++) {
2404 int from = offsets.at(i * 2);
2405 int to = offsets.at(i * 2 + 1);
2406 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2407 }
2408 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2409 result->set_length(Smi::FromInt(matches));
2410 return *result;
2411}
2412
2413
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414static Object* Runtime_NumberToRadixString(Arguments args) {
2415 NoHandleAllocation ha;
2416 ASSERT(args.length() == 2);
2417
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002418 // Fast case where the result is a one character string.
2419 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2420 int value = Smi::cast(args[0])->value();
2421 int radix = Smi::cast(args[1])->value();
2422 if (value >= 0 && value < radix) {
2423 RUNTIME_ASSERT(radix <= 36);
2424 // Character array used for conversion.
2425 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2426 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2427 }
2428 }
2429
2430 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002431 CONVERT_DOUBLE_CHECKED(value, args[0]);
2432 if (isnan(value)) {
2433 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2434 }
2435 if (isinf(value)) {
2436 if (value < 0) {
2437 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2438 }
2439 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2440 }
2441 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2442 int radix = FastD2I(radix_number);
2443 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2444 char* str = DoubleToRadixCString(value, radix);
2445 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2446 DeleteArray(str);
2447 return result;
2448}
2449
2450
2451static Object* Runtime_NumberToFixed(Arguments args) {
2452 NoHandleAllocation ha;
2453 ASSERT(args.length() == 2);
2454
2455 CONVERT_DOUBLE_CHECKED(value, args[0]);
2456 if (isnan(value)) {
2457 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2458 }
2459 if (isinf(value)) {
2460 if (value < 0) {
2461 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2462 }
2463 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2464 }
2465 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2466 int f = FastD2I(f_number);
2467 RUNTIME_ASSERT(f >= 0);
2468 char* str = DoubleToFixedCString(value, f);
2469 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2470 DeleteArray(str);
2471 return res;
2472}
2473
2474
2475static Object* Runtime_NumberToExponential(Arguments args) {
2476 NoHandleAllocation ha;
2477 ASSERT(args.length() == 2);
2478
2479 CONVERT_DOUBLE_CHECKED(value, args[0]);
2480 if (isnan(value)) {
2481 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2482 }
2483 if (isinf(value)) {
2484 if (value < 0) {
2485 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2486 }
2487 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2488 }
2489 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2490 int f = FastD2I(f_number);
2491 RUNTIME_ASSERT(f >= -1 && f <= 20);
2492 char* str = DoubleToExponentialCString(value, f);
2493 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2494 DeleteArray(str);
2495 return res;
2496}
2497
2498
2499static Object* Runtime_NumberToPrecision(Arguments args) {
2500 NoHandleAllocation ha;
2501 ASSERT(args.length() == 2);
2502
2503 CONVERT_DOUBLE_CHECKED(value, args[0]);
2504 if (isnan(value)) {
2505 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2506 }
2507 if (isinf(value)) {
2508 if (value < 0) {
2509 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2510 }
2511 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2512 }
2513 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2514 int f = FastD2I(f_number);
2515 RUNTIME_ASSERT(f >= 1 && f <= 21);
2516 char* str = DoubleToPrecisionCString(value, f);
2517 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2518 DeleteArray(str);
2519 return res;
2520}
2521
2522
2523// Returns a single character string where first character equals
2524// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002525static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002526 if (index < static_cast<uint32_t>(string->length())) {
2527 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002528 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002529 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002530 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002531 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002532}
2533
2534
2535Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2536 // Handle [] indexing on Strings
2537 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002538 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2539 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002540 }
2541
2542 // Handle [] indexing on String objects
2543 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002544 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2545 Handle<Object> result =
2546 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2547 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002548 }
2549
2550 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002551 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002552 return prototype->GetElement(index);
2553 }
2554
2555 return object->GetElement(index);
2556}
2557
2558
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002559Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2560 HandleScope scope;
2561
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002562 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002563 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002564 Handle<Object> error =
2565 Factory::NewTypeError("non_object_property_load",
2566 HandleVector(args, 2));
2567 return Top::Throw(*error);
2568 }
2569
2570 // Check if the given key is an array index.
2571 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002572 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002573 return GetElementOrCharAt(object, index);
2574 }
2575
2576 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002577 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002578 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002579 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002580 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002581 bool has_pending_exception = false;
2582 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002583 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002584 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002585 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002586 }
2587
ager@chromium.org32912102009-01-16 10:38:43 +00002588 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002589 // the element if so.
2590 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002591 return GetElementOrCharAt(object, index);
2592 } else {
2593 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002594 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002595 }
2596}
2597
2598
2599static Object* Runtime_GetProperty(Arguments args) {
2600 NoHandleAllocation ha;
2601 ASSERT(args.length() == 2);
2602
2603 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002604 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002605
2606 return Runtime::GetObjectProperty(object, key);
2607}
2608
2609
ager@chromium.org7c537e22008-10-16 08:43:32 +00002610
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002611// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002612static Object* Runtime_KeyedGetProperty(Arguments args) {
2613 NoHandleAllocation ha;
2614 ASSERT(args.length() == 2);
2615
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002616 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002617 // itself.
2618 //
2619 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002620 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002621 // global proxy object never has properties. This is the case
2622 // because the global proxy object forwards everything to its hidden
2623 // prototype including local lookups.
2624 //
2625 // Additionally, we need to make sure that we do not cache results
2626 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002627 if (args[0]->IsJSObject() &&
2628 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002629 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002630 args[1]->IsString()) {
2631 JSObject* receiver = JSObject::cast(args[0]);
2632 String* key = String::cast(args[1]);
2633 if (receiver->HasFastProperties()) {
2634 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002635 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002636 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2637 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002638 Object* value = receiver->FastPropertyAt(offset);
2639 return value->IsTheHole() ? Heap::undefined_value() : value;
2640 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002641 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002642 LookupResult result;
2643 receiver->LocalLookup(key, &result);
2644 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2645 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002646 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002647 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002648 }
2649 } else {
2650 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002651 StringDictionary* dictionary = receiver->property_dictionary();
2652 int entry = dictionary->FindEntry(key);
2653 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002654 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002655 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002656 if (!receiver->IsGlobalObject()) return value;
2657 value = JSGlobalPropertyCell::cast(value)->value();
2658 if (!value->IsTheHole()) return value;
2659 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002660 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002661 }
2662 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002663
2664 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002665 return Runtime::GetObjectProperty(args.at<Object>(0),
2666 args.at<Object>(1));
2667}
2668
2669
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002670Object* Runtime::SetObjectProperty(Handle<Object> object,
2671 Handle<Object> key,
2672 Handle<Object> value,
2673 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002674 HandleScope scope;
2675
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002676 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002677 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002678 Handle<Object> error =
2679 Factory::NewTypeError("non_object_property_store",
2680 HandleVector(args, 2));
2681 return Top::Throw(*error);
2682 }
2683
2684 // If the object isn't a JavaScript object, we ignore the store.
2685 if (!object->IsJSObject()) return *value;
2686
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002687 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2688
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002689 // Check if the given key is an array index.
2690 uint32_t index;
2691 if (Array::IndexFromObject(*key, &index)) {
2692 ASSERT(attr == NONE);
2693
2694 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2695 // of a string using [] notation. We need to support this too in
2696 // JavaScript.
2697 // In the case of a String object we just need to redirect the assignment to
2698 // the underlying string if the index is in range. Since the underlying
2699 // string does nothing with the assignment then we can ignore such
2700 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002701 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002702 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002703 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002704
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002705 Handle<Object> result = SetElement(js_object, index, value);
2706 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002707 return *value;
2708 }
2709
2710 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002711 Handle<Object> result;
2712 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002713 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002714 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002715 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002716 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002717 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002718 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002719 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002720 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002721 return *value;
2722 }
2723
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002724 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002725 bool has_pending_exception = false;
2726 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2727 if (has_pending_exception) return Failure::Exception();
2728 Handle<String> name = Handle<String>::cast(converted);
2729
2730 if (name->AsArrayIndex(&index)) {
2731 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002732 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002733 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002734 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002735 }
2736}
2737
2738
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002739Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2740 Handle<Object> key,
2741 Handle<Object> value,
2742 PropertyAttributes attr) {
2743 HandleScope scope;
2744
2745 // Check if the given key is an array index.
2746 uint32_t index;
2747 if (Array::IndexFromObject(*key, &index)) {
2748 ASSERT(attr == NONE);
2749
2750 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2751 // of a string using [] notation. We need to support this too in
2752 // JavaScript.
2753 // In the case of a String object we just need to redirect the assignment to
2754 // the underlying string if the index is in range. Since the underlying
2755 // string does nothing with the assignment then we can ignore such
2756 // assignments.
2757 if (js_object->IsStringObjectWithCharacterAt(index)) {
2758 return *value;
2759 }
2760
2761 return js_object->SetElement(index, *value);
2762 }
2763
2764 if (key->IsString()) {
2765 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2766 ASSERT(attr == NONE);
2767 return js_object->SetElement(index, *value);
2768 } else {
2769 Handle<String> key_string = Handle<String>::cast(key);
2770 key_string->TryFlattenIfNotFlat();
2771 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2772 *value,
2773 attr);
2774 }
2775 }
2776
2777 // Call-back into JavaScript to convert the key to a string.
2778 bool has_pending_exception = false;
2779 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2780 if (has_pending_exception) return Failure::Exception();
2781 Handle<String> name = Handle<String>::cast(converted);
2782
2783 if (name->AsArrayIndex(&index)) {
2784 ASSERT(attr == NONE);
2785 return js_object->SetElement(index, *value);
2786 } else {
2787 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2788 }
2789}
2790
2791
ager@chromium.orge2902be2009-06-08 12:21:35 +00002792Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2793 Handle<Object> key) {
2794 HandleScope scope;
2795
2796 // Check if the given key is an array index.
2797 uint32_t index;
2798 if (Array::IndexFromObject(*key, &index)) {
2799 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2800 // characters of a string using [] notation. In the case of a
2801 // String object we just need to redirect the deletion to the
2802 // underlying string if the index is in range. Since the
2803 // underlying string does nothing with the deletion, we can ignore
2804 // such deletions.
2805 if (js_object->IsStringObjectWithCharacterAt(index)) {
2806 return Heap::true_value();
2807 }
2808
2809 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2810 }
2811
2812 Handle<String> key_string;
2813 if (key->IsString()) {
2814 key_string = Handle<String>::cast(key);
2815 } else {
2816 // Call-back into JavaScript to convert the key to a string.
2817 bool has_pending_exception = false;
2818 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2819 if (has_pending_exception) return Failure::Exception();
2820 key_string = Handle<String>::cast(converted);
2821 }
2822
2823 key_string->TryFlattenIfNotFlat();
2824 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2825}
2826
2827
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002828static Object* Runtime_SetProperty(Arguments args) {
2829 NoHandleAllocation ha;
2830 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2831
2832 Handle<Object> object = args.at<Object>(0);
2833 Handle<Object> key = args.at<Object>(1);
2834 Handle<Object> value = args.at<Object>(2);
2835
2836 // Compute attributes.
2837 PropertyAttributes attributes = NONE;
2838 if (args.length() == 4) {
2839 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002840 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002842 RUNTIME_ASSERT(
2843 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2844 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002845 }
2846 return Runtime::SetObjectProperty(object, key, value, attributes);
2847}
2848
2849
2850// Set a local property, even if it is READ_ONLY. If the property does not
2851// exist, it will be added with attributes NONE.
2852static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2853 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002854 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002855 CONVERT_CHECKED(JSObject, object, args[0]);
2856 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002857 // Compute attributes.
2858 PropertyAttributes attributes = NONE;
2859 if (args.length() == 4) {
2860 CONVERT_CHECKED(Smi, value_obj, args[3]);
2861 int unchecked_value = value_obj->value();
2862 // Only attribute bits should be set.
2863 RUNTIME_ASSERT(
2864 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2865 attributes = static_cast<PropertyAttributes>(unchecked_value);
2866 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002867
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002868 return object->
2869 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002870}
2871
2872
2873static Object* Runtime_DeleteProperty(Arguments args) {
2874 NoHandleAllocation ha;
2875 ASSERT(args.length() == 2);
2876
2877 CONVERT_CHECKED(JSObject, object, args[0]);
2878 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002879 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002880}
2881
2882
ager@chromium.org9085a012009-05-11 19:22:57 +00002883static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2884 Handle<String> key) {
2885 if (object->HasLocalProperty(*key)) return Heap::true_value();
2886 // Handle hidden prototypes. If there's a hidden prototype above this thing
2887 // then we have to check it for properties, because they are supposed to
2888 // look like they are on this object.
2889 Handle<Object> proto(object->GetPrototype());
2890 if (proto->IsJSObject() &&
2891 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2892 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2893 }
2894 return Heap::false_value();
2895}
2896
2897
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002898static Object* Runtime_HasLocalProperty(Arguments args) {
2899 NoHandleAllocation ha;
2900 ASSERT(args.length() == 2);
2901 CONVERT_CHECKED(String, key, args[1]);
2902
ager@chromium.org9085a012009-05-11 19:22:57 +00002903 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002904 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002905 if (obj->IsJSObject()) {
2906 JSObject* object = JSObject::cast(obj);
2907 // Fast case - no interceptors.
2908 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2909 // Slow case. Either it's not there or we have an interceptor. We should
2910 // have handles for this kind of deal.
2911 HandleScope scope;
2912 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2913 Handle<String>(key));
2914 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915 // Well, there is one exception: Handle [] on strings.
2916 uint32_t index;
2917 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002918 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002919 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002920 return Heap::true_value();
2921 }
2922 }
2923 return Heap::false_value();
2924}
2925
2926
2927static Object* Runtime_HasProperty(Arguments args) {
2928 NoHandleAllocation na;
2929 ASSERT(args.length() == 2);
2930
2931 // Only JS objects can have properties.
2932 if (args[0]->IsJSObject()) {
2933 JSObject* object = JSObject::cast(args[0]);
2934 CONVERT_CHECKED(String, key, args[1]);
2935 if (object->HasProperty(key)) return Heap::true_value();
2936 }
2937 return Heap::false_value();
2938}
2939
2940
2941static Object* Runtime_HasElement(Arguments args) {
2942 NoHandleAllocation na;
2943 ASSERT(args.length() == 2);
2944
2945 // Only JS objects can have elements.
2946 if (args[0]->IsJSObject()) {
2947 JSObject* object = JSObject::cast(args[0]);
2948 CONVERT_CHECKED(Smi, index_obj, args[1]);
2949 uint32_t index = index_obj->value();
2950 if (object->HasElement(index)) return Heap::true_value();
2951 }
2952 return Heap::false_value();
2953}
2954
2955
2956static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2957 NoHandleAllocation ha;
2958 ASSERT(args.length() == 2);
2959
2960 CONVERT_CHECKED(JSObject, object, args[0]);
2961 CONVERT_CHECKED(String, key, args[1]);
2962
2963 uint32_t index;
2964 if (key->AsArrayIndex(&index)) {
2965 return Heap::ToBoolean(object->HasElement(index));
2966 }
2967
ager@chromium.org870a0b62008-11-04 11:43:05 +00002968 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2969 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002970}
2971
2972
2973static Object* Runtime_GetPropertyNames(Arguments args) {
2974 HandleScope scope;
2975 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002976 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002977 return *GetKeysFor(object);
2978}
2979
2980
2981// Returns either a FixedArray as Runtime_GetPropertyNames,
2982// or, if the given object has an enum cache that contains
2983// all enumerable properties of the object and its prototypes
2984// have none, the map of the object. This is used to speed up
2985// the check for deletions during a for-in.
2986static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2987 ASSERT(args.length() == 1);
2988
2989 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2990
2991 if (raw_object->IsSimpleEnum()) return raw_object->map();
2992
2993 HandleScope scope;
2994 Handle<JSObject> object(raw_object);
2995 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2996
2997 // Test again, since cache may have been built by preceding call.
2998 if (object->IsSimpleEnum()) return object->map();
2999
3000 return *content;
3001}
3002
3003
3004static Object* Runtime_GetArgumentsProperty(Arguments args) {
3005 NoHandleAllocation ha;
3006 ASSERT(args.length() == 1);
3007
3008 // Compute the frame holding the arguments.
3009 JavaScriptFrameIterator it;
3010 it.AdvanceToArgumentsFrame();
3011 JavaScriptFrame* frame = it.frame();
3012
3013 // Get the actual number of provided arguments.
3014 const uint32_t n = frame->GetProvidedParametersCount();
3015
3016 // Try to convert the key to an index. If successful and within
3017 // index return the the argument from the frame.
3018 uint32_t index;
3019 if (Array::IndexFromObject(args[0], &index) && index < n) {
3020 return frame->GetParameter(index);
3021 }
3022
3023 // Convert the key to a string.
3024 HandleScope scope;
3025 bool exception = false;
3026 Handle<Object> converted =
3027 Execution::ToString(args.at<Object>(0), &exception);
3028 if (exception) return Failure::Exception();
3029 Handle<String> key = Handle<String>::cast(converted);
3030
3031 // Try to convert the string key into an array index.
3032 if (key->AsArrayIndex(&index)) {
3033 if (index < n) {
3034 return frame->GetParameter(index);
3035 } else {
3036 return Top::initial_object_prototype()->GetElement(index);
3037 }
3038 }
3039
3040 // Handle special arguments properties.
3041 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3042 if (key->Equals(Heap::callee_symbol())) return frame->function();
3043
3044 // Lookup in the initial Object.prototype object.
3045 return Top::initial_object_prototype()->GetProperty(*key);
3046}
3047
3048
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003049static Object* Runtime_ToFastProperties(Arguments args) {
3050 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003051 Handle<Object> object = args.at<Object>(0);
3052 if (object->IsJSObject()) {
3053 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3054 js_object->TransformToFastProperties(0);
3055 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003056 return *object;
3057}
3058
3059
3060static Object* Runtime_ToSlowProperties(Arguments args) {
3061 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003062 Handle<Object> object = args.at<Object>(0);
3063 if (object->IsJSObject()) {
3064 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003065 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003066 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003067 return *object;
3068}
3069
3070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003071static Object* Runtime_ToBool(Arguments args) {
3072 NoHandleAllocation ha;
3073 ASSERT(args.length() == 1);
3074
3075 return args[0]->ToBoolean();
3076}
3077
3078
3079// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3080// Possible optimizations: put the type string into the oddballs.
3081static Object* Runtime_Typeof(Arguments args) {
3082 NoHandleAllocation ha;
3083
3084 Object* obj = args[0];
3085 if (obj->IsNumber()) return Heap::number_symbol();
3086 HeapObject* heap_obj = HeapObject::cast(obj);
3087
3088 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003089 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003090
3091 InstanceType instance_type = heap_obj->map()->instance_type();
3092 if (instance_type < FIRST_NONSTRING_TYPE) {
3093 return Heap::string_symbol();
3094 }
3095
3096 switch (instance_type) {
3097 case ODDBALL_TYPE:
3098 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3099 return Heap::boolean_symbol();
3100 }
3101 if (heap_obj->IsNull()) {
3102 return Heap::object_symbol();
3103 }
3104 ASSERT(heap_obj->IsUndefined());
3105 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003106 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003107 return Heap::function_symbol();
3108 default:
3109 // For any kind of object not handled above, the spec rule for
3110 // host objects gives that it is okay to return "object"
3111 return Heap::object_symbol();
3112 }
3113}
3114
3115
3116static Object* Runtime_StringToNumber(Arguments args) {
3117 NoHandleAllocation ha;
3118 ASSERT(args.length() == 1);
3119 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003120 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003121 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3122}
3123
3124
3125static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3126 NoHandleAllocation ha;
3127 ASSERT(args.length() == 1);
3128
3129 CONVERT_CHECKED(JSArray, codes, args[0]);
3130 int length = Smi::cast(codes->length())->value();
3131
3132 // Check if the string can be ASCII.
3133 int i;
3134 for (i = 0; i < length; i++) {
3135 Object* element = codes->GetElement(i);
3136 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3137 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3138 break;
3139 }
3140
3141 Object* object = NULL;
3142 if (i == length) { // The string is ASCII.
3143 object = Heap::AllocateRawAsciiString(length);
3144 } else { // The string is not ASCII.
3145 object = Heap::AllocateRawTwoByteString(length);
3146 }
3147
3148 if (object->IsFailure()) return object;
3149 String* result = String::cast(object);
3150 for (int i = 0; i < length; i++) {
3151 Object* element = codes->GetElement(i);
3152 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003153 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003154 }
3155 return result;
3156}
3157
3158
3159// kNotEscaped is generated by the following:
3160//
3161// #!/bin/perl
3162// for (my $i = 0; $i < 256; $i++) {
3163// print "\n" if $i % 16 == 0;
3164// my $c = chr($i);
3165// my $escaped = 1;
3166// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3167// print $escaped ? "0, " : "1, ";
3168// }
3169
3170
3171static bool IsNotEscaped(uint16_t character) {
3172 // Only for 8 bit characters, the rest are always escaped (in a different way)
3173 ASSERT(character < 256);
3174 static const char kNotEscaped[256] = {
3175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3179 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3181 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3182 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3183 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3184 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3185 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3187 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3188 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3189 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3190 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3191 };
3192 return kNotEscaped[character] != 0;
3193}
3194
3195
3196static Object* Runtime_URIEscape(Arguments args) {
3197 const char hex_chars[] = "0123456789ABCDEF";
3198 NoHandleAllocation ha;
3199 ASSERT(args.length() == 1);
3200 CONVERT_CHECKED(String, source, args[0]);
3201
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003202 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003203
3204 int escaped_length = 0;
3205 int length = source->length();
3206 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003207 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003208 buffer->Reset(source);
3209 while (buffer->has_more()) {
3210 uint16_t character = buffer->GetNext();
3211 if (character >= 256) {
3212 escaped_length += 6;
3213 } else if (IsNotEscaped(character)) {
3214 escaped_length++;
3215 } else {
3216 escaped_length += 3;
3217 }
3218 // We don't allow strings that are longer than Smi range.
3219 if (!Smi::IsValid(escaped_length)) {
3220 Top::context()->mark_out_of_memory();
3221 return Failure::OutOfMemoryException();
3222 }
3223 }
3224 }
3225 // No length change implies no change. Return original string if no change.
3226 if (escaped_length == length) {
3227 return source;
3228 }
3229 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3230 if (o->IsFailure()) return o;
3231 String* destination = String::cast(o);
3232 int dest_position = 0;
3233
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003234 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003235 buffer->Rewind();
3236 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003237 uint16_t chr = buffer->GetNext();
3238 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003239 destination->Set(dest_position, '%');
3240 destination->Set(dest_position+1, 'u');
3241 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3242 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3243 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3244 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003245 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003246 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003247 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003248 dest_position++;
3249 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003250 destination->Set(dest_position, '%');
3251 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3252 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003253 dest_position += 3;
3254 }
3255 }
3256 return destination;
3257}
3258
3259
3260static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3261 static const signed char kHexValue['g'] = {
3262 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3263 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3264 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3265 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3266 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3267 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3268 -1, 10, 11, 12, 13, 14, 15 };
3269
3270 if (character1 > 'f') return -1;
3271 int hi = kHexValue[character1];
3272 if (hi == -1) return -1;
3273 if (character2 > 'f') return -1;
3274 int lo = kHexValue[character2];
3275 if (lo == -1) return -1;
3276 return (hi << 4) + lo;
3277}
3278
3279
ager@chromium.org870a0b62008-11-04 11:43:05 +00003280static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003281 int i,
3282 int length,
3283 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003284 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003285 int32_t hi = 0;
3286 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003287 if (character == '%' &&
3288 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003289 source->Get(i + 1) == 'u' &&
3290 (hi = TwoDigitHex(source->Get(i + 2),
3291 source->Get(i + 3))) != -1 &&
3292 (lo = TwoDigitHex(source->Get(i + 4),
3293 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003294 *step = 6;
3295 return (hi << 8) + lo;
3296 } else if (character == '%' &&
3297 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003298 (lo = TwoDigitHex(source->Get(i + 1),
3299 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003300 *step = 3;
3301 return lo;
3302 } else {
3303 *step = 1;
3304 return character;
3305 }
3306}
3307
3308
3309static Object* Runtime_URIUnescape(Arguments args) {
3310 NoHandleAllocation ha;
3311 ASSERT(args.length() == 1);
3312 CONVERT_CHECKED(String, source, args[0]);
3313
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003314 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003315
3316 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003317 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003318
3319 int unescaped_length = 0;
3320 for (int i = 0; i < length; unescaped_length++) {
3321 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003322 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003323 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003324 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003325 i += step;
3326 }
3327
3328 // No length change implies no change. Return original string if no change.
3329 if (unescaped_length == length)
3330 return source;
3331
3332 Object* o = ascii ?
3333 Heap::AllocateRawAsciiString(unescaped_length) :
3334 Heap::AllocateRawTwoByteString(unescaped_length);
3335 if (o->IsFailure()) return o;
3336 String* destination = String::cast(o);
3337
3338 int dest_position = 0;
3339 for (int i = 0; i < length; dest_position++) {
3340 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003341 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003342 i += step;
3343 }
3344 return destination;
3345}
3346
3347
3348static Object* Runtime_StringParseInt(Arguments args) {
3349 NoHandleAllocation ha;
3350
3351 CONVERT_CHECKED(String, s, args[0]);
3352 CONVERT_DOUBLE_CHECKED(n, args[1]);
3353 int radix = FastD2I(n);
3354
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003355 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003356
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003357 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003358 int i;
3359
3360 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003361 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003362 if (i == len) return Heap::nan_value();
3363
3364 // Compute the sign (default to +).
3365 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003366 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003367 sign = -1;
3368 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003369 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003370 i++;
3371 }
3372
3373 // Compute the radix if 0.
3374 if (radix == 0) {
3375 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003376 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003377 radix = 8;
3378 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003379 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003380 if (c == 'x' || c == 'X') {
3381 radix = 16;
3382 i += 2;
3383 }
3384 }
3385 }
3386 } else if (radix == 16) {
3387 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003388 if (i + 1 < len && s->Get(i) == '0') {
3389 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003390 if (c == 'x' || c == 'X') i += 2;
3391 }
3392 }
3393
3394 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3395 double value;
3396 int end_index = StringToInt(s, i, radix, &value);
3397 if (end_index != i) {
3398 return Heap::NumberFromDouble(sign * value);
3399 }
3400 return Heap::nan_value();
3401}
3402
3403
3404static Object* Runtime_StringParseFloat(Arguments args) {
3405 NoHandleAllocation ha;
3406 CONVERT_CHECKED(String, str, args[0]);
3407
3408 // ECMA-262 section 15.1.2.3, empty string is NaN
3409 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3410
3411 // Create a number object from the value.
3412 return Heap::NumberFromDouble(value);
3413}
3414
3415
3416static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3417static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3418
3419
3420template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003421static Object* ConvertCaseHelper(String* s,
3422 int length,
3423 int input_string_length,
3424 unibrow::Mapping<Converter, 128>* mapping) {
3425 // We try this twice, once with the assumption that the result is no longer
3426 // than the input and, if that assumption breaks, again with the exact
3427 // length. This may not be pretty, but it is nicer than what was here before
3428 // and I hereby claim my vaffel-is.
3429 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003430 // Allocate the resulting string.
3431 //
3432 // NOTE: This assumes that the upper/lower case of an ascii
3433 // character is also ascii. This is currently the case, but it
3434 // might break in the future if we implement more context and locale
3435 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003436 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003437 ? Heap::AllocateRawAsciiString(length)
3438 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003439 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003440 String* result = String::cast(o);
3441 bool has_changed_character = false;
3442
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003443 // Convert all characters to upper case, assuming that they will fit
3444 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003445 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003446 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003447 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003448 // We can assume that the string is not empty
3449 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003450 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003451 bool has_next = buffer->has_more();
3452 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003453 int char_length = mapping->get(current, next, chars);
3454 if (char_length == 0) {
3455 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003456 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003457 i++;
3458 } else if (char_length == 1) {
3459 // Common case: converting the letter resulted in one character.
3460 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003461 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003462 has_changed_character = true;
3463 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003464 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003465 // We've assumed that the result would be as long as the
3466 // input but here is a character that converts to several
3467 // characters. No matter, we calculate the exact length
3468 // of the result and try the whole thing again.
3469 //
3470 // Note that this leaves room for optimization. We could just
3471 // memcpy what we already have to the result string. Also,
3472 // the result string is the last object allocated we could
3473 // "realloc" it and probably, in the vast majority of cases,
3474 // extend the existing string to be able to hold the full
3475 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003476 int next_length = 0;
3477 if (has_next) {
3478 next_length = mapping->get(next, 0, chars);
3479 if (next_length == 0) next_length = 1;
3480 }
3481 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003482 while (buffer->has_more()) {
3483 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003484 // NOTE: we use 0 as the next character here because, while
3485 // the next character may affect what a character converts to,
3486 // it does not in any case affect the length of what it convert
3487 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003488 int char_length = mapping->get(current, 0, chars);
3489 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003490 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003491 if (current_length > Smi::kMaxValue) {
3492 Top::context()->mark_out_of_memory();
3493 return Failure::OutOfMemoryException();
3494 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003495 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003496 // Try again with the real length.
3497 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003498 } else {
3499 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003500 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003501 i++;
3502 }
3503 has_changed_character = true;
3504 }
3505 current = next;
3506 }
3507 if (has_changed_character) {
3508 return result;
3509 } else {
3510 // If we didn't actually change anything in doing the conversion
3511 // we simple return the result and let the converted string
3512 // become garbage; there is no reason to keep two identical strings
3513 // alive.
3514 return s;
3515 }
3516}
3517
3518
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003519template <class Converter>
3520static Object* ConvertCase(Arguments args,
3521 unibrow::Mapping<Converter, 128>* mapping) {
3522 NoHandleAllocation ha;
3523
3524 CONVERT_CHECKED(String, s, args[0]);
3525 s->TryFlattenIfNotFlat();
3526
3527 int input_string_length = s->length();
3528 // Assume that the string is not empty; we need this assumption later
3529 if (input_string_length == 0) return s;
3530 int length = input_string_length;
3531
3532 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3533 if (answer->IsSmi()) {
3534 // Retry with correct length.
3535 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3536 }
3537 return answer; // This may be a failure.
3538}
3539
3540
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003541static Object* Runtime_StringToLowerCase(Arguments args) {
3542 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3543}
3544
3545
3546static Object* Runtime_StringToUpperCase(Arguments args) {
3547 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3548}
3549
3550
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003551bool Runtime::IsUpperCaseChar(uint16_t ch) {
3552 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3553 int char_length = to_upper_mapping.get(ch, 0, chars);
3554 return char_length == 0;
3555}
3556
3557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003558static Object* Runtime_NumberToString(Arguments args) {
3559 NoHandleAllocation ha;
3560 ASSERT(args.length() == 1);
3561
3562 Object* number = args[0];
3563 RUNTIME_ASSERT(number->IsNumber());
3564
3565 Object* cached = Heap::GetNumberStringCache(number);
3566 if (cached != Heap::undefined_value()) {
3567 return cached;
3568 }
3569
3570 char arr[100];
3571 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3572 const char* str;
3573 if (number->IsSmi()) {
3574 int num = Smi::cast(number)->value();
3575 str = IntToCString(num, buffer);
3576 } else {
3577 double num = HeapNumber::cast(number)->value();
3578 str = DoubleToCString(num, buffer);
3579 }
3580 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3581
3582 if (!result->IsFailure()) {
3583 Heap::SetNumberStringCache(number, String::cast(result));
3584 }
3585 return result;
3586}
3587
3588
3589static Object* Runtime_NumberToInteger(Arguments args) {
3590 NoHandleAllocation ha;
3591 ASSERT(args.length() == 1);
3592
3593 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003594 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003595 CONVERT_DOUBLE_CHECKED(number, obj);
3596 return Heap::NumberFromDouble(DoubleToInteger(number));
3597}
3598
3599
3600static Object* Runtime_NumberToJSUint32(Arguments args) {
3601 NoHandleAllocation ha;
3602 ASSERT(args.length() == 1);
3603
3604 Object* obj = args[0];
3605 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3606 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3607 return Heap::NumberFromUint32(number);
3608}
3609
3610
3611static Object* Runtime_NumberToJSInt32(Arguments args) {
3612 NoHandleAllocation ha;
3613 ASSERT(args.length() == 1);
3614
3615 Object* obj = args[0];
3616 if (obj->IsSmi()) return obj;
3617 CONVERT_DOUBLE_CHECKED(number, obj);
3618 return Heap::NumberFromInt32(DoubleToInt32(number));
3619}
3620
3621
ager@chromium.org870a0b62008-11-04 11:43:05 +00003622// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3623// a small integer.
3624static Object* Runtime_NumberToSmi(Arguments args) {
3625 NoHandleAllocation ha;
3626 ASSERT(args.length() == 1);
3627
3628 Object* obj = args[0];
3629 if (obj->IsSmi()) {
3630 return obj;
3631 }
3632 if (obj->IsHeapNumber()) {
3633 double value = HeapNumber::cast(obj)->value();
3634 int int_value = FastD2I(value);
3635 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3636 return Smi::FromInt(int_value);
3637 }
3638 }
3639 return Heap::nan_value();
3640}
3641
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003643static Object* Runtime_NumberAdd(Arguments args) {
3644 NoHandleAllocation ha;
3645 ASSERT(args.length() == 2);
3646
3647 CONVERT_DOUBLE_CHECKED(x, args[0]);
3648 CONVERT_DOUBLE_CHECKED(y, args[1]);
3649 return Heap::AllocateHeapNumber(x + y);
3650}
3651
3652
3653static Object* Runtime_NumberSub(Arguments args) {
3654 NoHandleAllocation ha;
3655 ASSERT(args.length() == 2);
3656
3657 CONVERT_DOUBLE_CHECKED(x, args[0]);
3658 CONVERT_DOUBLE_CHECKED(y, args[1]);
3659 return Heap::AllocateHeapNumber(x - y);
3660}
3661
3662
3663static Object* Runtime_NumberMul(Arguments args) {
3664 NoHandleAllocation ha;
3665 ASSERT(args.length() == 2);
3666
3667 CONVERT_DOUBLE_CHECKED(x, args[0]);
3668 CONVERT_DOUBLE_CHECKED(y, args[1]);
3669 return Heap::AllocateHeapNumber(x * y);
3670}
3671
3672
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003673static Object* Runtime_NumberUnaryMinus(Arguments args) {
3674 NoHandleAllocation ha;
3675 ASSERT(args.length() == 1);
3676
3677 CONVERT_DOUBLE_CHECKED(x, args[0]);
3678 return Heap::AllocateHeapNumber(-x);
3679}
3680
3681
3682static Object* Runtime_NumberDiv(Arguments args) {
3683 NoHandleAllocation ha;
3684 ASSERT(args.length() == 2);
3685
3686 CONVERT_DOUBLE_CHECKED(x, args[0]);
3687 CONVERT_DOUBLE_CHECKED(y, args[1]);
3688 return Heap::NewNumberFromDouble(x / y);
3689}
3690
3691
3692static Object* Runtime_NumberMod(Arguments args) {
3693 NoHandleAllocation ha;
3694 ASSERT(args.length() == 2);
3695
3696 CONVERT_DOUBLE_CHECKED(x, args[0]);
3697 CONVERT_DOUBLE_CHECKED(y, args[1]);
3698
3699#ifdef WIN32
3700 // Workaround MS fmod bugs. ECMA-262 says:
3701 // dividend is finite and divisor is an infinity => result equals dividend
3702 // dividend is a zero and divisor is nonzero finite => result equals dividend
3703 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3704 !(x == 0 && (y != 0 && isfinite(y))))
3705#endif
3706 x = fmod(x, y);
3707 // NewNumberFromDouble may return a Smi instead of a Number object
3708 return Heap::NewNumberFromDouble(x);
3709}
3710
3711
3712static Object* Runtime_StringAdd(Arguments args) {
3713 NoHandleAllocation ha;
3714 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003715 CONVERT_CHECKED(String, str1, args[0]);
3716 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003717 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003718}
3719
3720
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003721template<typename sinkchar>
3722static inline void StringBuilderConcatHelper(String* special,
3723 sinkchar* sink,
3724 FixedArray* fixed_array,
3725 int array_length) {
3726 int position = 0;
3727 for (int i = 0; i < array_length; i++) {
3728 Object* element = fixed_array->get(i);
3729 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003730 int encoded_slice = Smi::cast(element)->value();
3731 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3732 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003733 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003734 sink + position,
3735 pos,
3736 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003737 position += len;
3738 } else {
3739 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003740 int element_length = string->length();
3741 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003742 position += element_length;
3743 }
3744 }
3745}
3746
3747
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003748static Object* Runtime_StringBuilderConcat(Arguments args) {
3749 NoHandleAllocation ha;
3750 ASSERT(args.length() == 2);
3751 CONVERT_CHECKED(JSArray, array, args[0]);
3752 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003753 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003754 Object* smi_array_length = array->length();
3755 if (!smi_array_length->IsSmi()) {
3756 Top::context()->mark_out_of_memory();
3757 return Failure::OutOfMemoryException();
3758 }
3759 int array_length = Smi::cast(smi_array_length)->value();
3760 if (!array->HasFastElements()) {
3761 return Top::Throw(Heap::illegal_argument_symbol());
3762 }
3763 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003764 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003765 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003766 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003767
3768 if (array_length == 0) {
3769 return Heap::empty_string();
3770 } else if (array_length == 1) {
3771 Object* first = fixed_array->get(0);
3772 if (first->IsString()) return first;
3773 }
3774
ager@chromium.org5ec48922009-05-05 07:25:34 +00003775 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003776 int position = 0;
3777 for (int i = 0; i < array_length; i++) {
3778 Object* elt = fixed_array->get(i);
3779 if (elt->IsSmi()) {
3780 int len = Smi::cast(elt)->value();
3781 int pos = len >> 11;
3782 len &= 0x7ff;
3783 if (pos + len > special_length) {
3784 return Top::Throw(Heap::illegal_argument_symbol());
3785 }
3786 position += len;
3787 } else if (elt->IsString()) {
3788 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003789 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790 if (!Smi::IsValid(element_length + position)) {
3791 Top::context()->mark_out_of_memory();
3792 return Failure::OutOfMemoryException();
3793 }
3794 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003795 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003796 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003797 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003798 } else {
3799 return Top::Throw(Heap::illegal_argument_symbol());
3800 }
3801 }
3802
3803 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003804 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003805
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003806 if (ascii) {
3807 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003808 if (object->IsFailure()) return object;
3809 SeqAsciiString* answer = SeqAsciiString::cast(object);
3810 StringBuilderConcatHelper(special,
3811 answer->GetChars(),
3812 fixed_array,
3813 array_length);
3814 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003815 } else {
3816 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003817 if (object->IsFailure()) return object;
3818 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3819 StringBuilderConcatHelper(special,
3820 answer->GetChars(),
3821 fixed_array,
3822 array_length);
3823 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003824 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003825}
3826
3827
3828static Object* Runtime_NumberOr(Arguments args) {
3829 NoHandleAllocation ha;
3830 ASSERT(args.length() == 2);
3831
3832 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3833 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3834 return Heap::NumberFromInt32(x | y);
3835}
3836
3837
3838static Object* Runtime_NumberAnd(Arguments args) {
3839 NoHandleAllocation ha;
3840 ASSERT(args.length() == 2);
3841
3842 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3843 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3844 return Heap::NumberFromInt32(x & y);
3845}
3846
3847
3848static Object* Runtime_NumberXor(Arguments args) {
3849 NoHandleAllocation ha;
3850 ASSERT(args.length() == 2);
3851
3852 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3853 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3854 return Heap::NumberFromInt32(x ^ y);
3855}
3856
3857
3858static Object* Runtime_NumberNot(Arguments args) {
3859 NoHandleAllocation ha;
3860 ASSERT(args.length() == 1);
3861
3862 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3863 return Heap::NumberFromInt32(~x);
3864}
3865
3866
3867static Object* Runtime_NumberShl(Arguments args) {
3868 NoHandleAllocation ha;
3869 ASSERT(args.length() == 2);
3870
3871 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3872 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3873 return Heap::NumberFromInt32(x << (y & 0x1f));
3874}
3875
3876
3877static Object* Runtime_NumberShr(Arguments args) {
3878 NoHandleAllocation ha;
3879 ASSERT(args.length() == 2);
3880
3881 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3882 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3883 return Heap::NumberFromUint32(x >> (y & 0x1f));
3884}
3885
3886
3887static Object* Runtime_NumberSar(Arguments args) {
3888 NoHandleAllocation ha;
3889 ASSERT(args.length() == 2);
3890
3891 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3892 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3893 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3894}
3895
3896
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003897static Object* Runtime_NumberEquals(Arguments args) {
3898 NoHandleAllocation ha;
3899 ASSERT(args.length() == 2);
3900
3901 CONVERT_DOUBLE_CHECKED(x, args[0]);
3902 CONVERT_DOUBLE_CHECKED(y, args[1]);
3903 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3904 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3905 if (x == y) return Smi::FromInt(EQUAL);
3906 Object* result;
3907 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3908 result = Smi::FromInt(EQUAL);
3909 } else {
3910 result = Smi::FromInt(NOT_EQUAL);
3911 }
3912 return result;
3913}
3914
3915
3916static Object* Runtime_StringEquals(Arguments args) {
3917 NoHandleAllocation ha;
3918 ASSERT(args.length() == 2);
3919
3920 CONVERT_CHECKED(String, x, args[0]);
3921 CONVERT_CHECKED(String, y, args[1]);
3922
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003923 bool not_equal = !x->Equals(y);
3924 // This is slightly convoluted because the value that signifies
3925 // equality is 0 and inequality is 1 so we have to negate the result
3926 // from String::Equals.
3927 ASSERT(not_equal == 0 || not_equal == 1);
3928 STATIC_CHECK(EQUAL == 0);
3929 STATIC_CHECK(NOT_EQUAL == 1);
3930 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003931}
3932
3933
3934static Object* Runtime_NumberCompare(Arguments args) {
3935 NoHandleAllocation ha;
3936 ASSERT(args.length() == 3);
3937
3938 CONVERT_DOUBLE_CHECKED(x, args[0]);
3939 CONVERT_DOUBLE_CHECKED(y, args[1]);
3940 if (isnan(x) || isnan(y)) return args[2];
3941 if (x == y) return Smi::FromInt(EQUAL);
3942 if (isless(x, y)) return Smi::FromInt(LESS);
3943 return Smi::FromInt(GREATER);
3944}
3945
3946
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003947// Compare two Smis as if they were converted to strings and then
3948// compared lexicographically.
3949static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3950 NoHandleAllocation ha;
3951 ASSERT(args.length() == 2);
3952
3953 // Arrays for the individual characters of the two Smis. Smis are
3954 // 31 bit integers and 10 decimal digits are therefore enough.
3955 static int x_elms[10];
3956 static int y_elms[10];
3957
3958 // Extract the integer values from the Smis.
3959 CONVERT_CHECKED(Smi, x, args[0]);
3960 CONVERT_CHECKED(Smi, y, args[1]);
3961 int x_value = x->value();
3962 int y_value = y->value();
3963
3964 // If the integers are equal so are the string representations.
3965 if (x_value == y_value) return Smi::FromInt(EQUAL);
3966
3967 // If one of the integers are zero the normal integer order is the
3968 // same as the lexicographic order of the string representations.
3969 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3970
ager@chromium.org32912102009-01-16 10:38:43 +00003971 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003972 // smallest because the char code of '-' is less than the char code
3973 // of any digit. Otherwise, we make both values positive.
3974 if (x_value < 0 || y_value < 0) {
3975 if (y_value >= 0) return Smi::FromInt(LESS);
3976 if (x_value >= 0) return Smi::FromInt(GREATER);
3977 x_value = -x_value;
3978 y_value = -y_value;
3979 }
3980
3981 // Convert the integers to arrays of their decimal digits.
3982 int x_index = 0;
3983 int y_index = 0;
3984 while (x_value > 0) {
3985 x_elms[x_index++] = x_value % 10;
3986 x_value /= 10;
3987 }
3988 while (y_value > 0) {
3989 y_elms[y_index++] = y_value % 10;
3990 y_value /= 10;
3991 }
3992
3993 // Loop through the arrays of decimal digits finding the first place
3994 // where they differ.
3995 while (--x_index >= 0 && --y_index >= 0) {
3996 int diff = x_elms[x_index] - y_elms[y_index];
3997 if (diff != 0) return Smi::FromInt(diff);
3998 }
3999
4000 // If one array is a suffix of the other array, the longest array is
4001 // the representation of the largest of the Smis in the
4002 // lexicographic ordering.
4003 return Smi::FromInt(x_index - y_index);
4004}
4005
4006
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004007static Object* Runtime_StringCompare(Arguments args) {
4008 NoHandleAllocation ha;
4009 ASSERT(args.length() == 2);
4010
4011 CONVERT_CHECKED(String, x, args[0]);
4012 CONVERT_CHECKED(String, y, args[1]);
4013
4014 // A few fast case tests before we flatten.
4015 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004016 if (y->length() == 0) {
4017 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004018 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004019 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004020 return Smi::FromInt(LESS);
4021 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004022
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004023 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004024 if (d < 0) return Smi::FromInt(LESS);
4025 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004026
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004027 x->TryFlattenIfNotFlat();
4028 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004029
4030 static StringInputBuffer bufx;
4031 static StringInputBuffer bufy;
4032 bufx.Reset(x);
4033 bufy.Reset(y);
4034 while (bufx.has_more() && bufy.has_more()) {
4035 int d = bufx.GetNext() - bufy.GetNext();
4036 if (d < 0) return Smi::FromInt(LESS);
4037 else if (d > 0) return Smi::FromInt(GREATER);
4038 }
4039
4040 // x is (non-trivial) prefix of y:
4041 if (bufy.has_more()) return Smi::FromInt(LESS);
4042 // y is prefix of x:
4043 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4044}
4045
4046
4047static Object* Runtime_Math_abs(Arguments args) {
4048 NoHandleAllocation ha;
4049 ASSERT(args.length() == 1);
4050
4051 CONVERT_DOUBLE_CHECKED(x, args[0]);
4052 return Heap::AllocateHeapNumber(fabs(x));
4053}
4054
4055
4056static Object* Runtime_Math_acos(Arguments args) {
4057 NoHandleAllocation ha;
4058 ASSERT(args.length() == 1);
4059
4060 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004061 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004062}
4063
4064
4065static Object* Runtime_Math_asin(Arguments args) {
4066 NoHandleAllocation ha;
4067 ASSERT(args.length() == 1);
4068
4069 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004070 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004071}
4072
4073
4074static Object* Runtime_Math_atan(Arguments args) {
4075 NoHandleAllocation ha;
4076 ASSERT(args.length() == 1);
4077
4078 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004079 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004080}
4081
4082
4083static Object* Runtime_Math_atan2(Arguments args) {
4084 NoHandleAllocation ha;
4085 ASSERT(args.length() == 2);
4086
4087 CONVERT_DOUBLE_CHECKED(x, args[0]);
4088 CONVERT_DOUBLE_CHECKED(y, args[1]);
4089 double result;
4090 if (isinf(x) && isinf(y)) {
4091 // Make sure that the result in case of two infinite arguments
4092 // is a multiple of Pi / 4. The sign of the result is determined
4093 // by the first argument (x) and the sign of the second argument
4094 // determines the multiplier: one or three.
4095 static double kPiDividedBy4 = 0.78539816339744830962;
4096 int multiplier = (x < 0) ? -1 : 1;
4097 if (y < 0) multiplier *= 3;
4098 result = multiplier * kPiDividedBy4;
4099 } else {
4100 result = atan2(x, y);
4101 }
4102 return Heap::AllocateHeapNumber(result);
4103}
4104
4105
4106static Object* Runtime_Math_ceil(Arguments args) {
4107 NoHandleAllocation ha;
4108 ASSERT(args.length() == 1);
4109
4110 CONVERT_DOUBLE_CHECKED(x, args[0]);
4111 return Heap::NumberFromDouble(ceiling(x));
4112}
4113
4114
4115static Object* Runtime_Math_cos(Arguments args) {
4116 NoHandleAllocation ha;
4117 ASSERT(args.length() == 1);
4118
4119 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004120 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004121}
4122
4123
4124static Object* Runtime_Math_exp(Arguments args) {
4125 NoHandleAllocation ha;
4126 ASSERT(args.length() == 1);
4127
4128 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004129 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004130}
4131
4132
4133static Object* Runtime_Math_floor(Arguments args) {
4134 NoHandleAllocation ha;
4135 ASSERT(args.length() == 1);
4136
4137 CONVERT_DOUBLE_CHECKED(x, args[0]);
4138 return Heap::NumberFromDouble(floor(x));
4139}
4140
4141
4142static Object* Runtime_Math_log(Arguments args) {
4143 NoHandleAllocation ha;
4144 ASSERT(args.length() == 1);
4145
4146 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004147 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004148}
4149
4150
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004151// Helper function to compute x^y, where y is known to be an
4152// integer. Uses binary decomposition to limit the number of
4153// multiplications; see the discussion in "Hacker's Delight" by Henry
4154// S. Warren, Jr., figure 11-6, page 213.
4155static double powi(double x, int y) {
4156 ASSERT(y != kMinInt);
4157 unsigned n = (y < 0) ? -y : y;
4158 double m = x;
4159 double p = 1;
4160 while (true) {
4161 if ((n & 1) != 0) p *= m;
4162 n >>= 1;
4163 if (n == 0) {
4164 if (y < 0) {
4165 // Unfortunately, we have to be careful when p has reached
4166 // infinity in the computation, because sometimes the higher
4167 // internal precision in the pow() implementation would have
4168 // given us a finite p. This happens very rarely.
4169 double result = 1.0 / p;
4170 return (result == 0 && isinf(p))
4171 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4172 : result;
4173 } else {
4174 return p;
4175 }
4176 }
4177 m *= m;
4178 }
4179}
4180
4181
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004182static Object* Runtime_Math_pow(Arguments args) {
4183 NoHandleAllocation ha;
4184 ASSERT(args.length() == 2);
4185
4186 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004187
4188 // If the second argument is a smi, it is much faster to call the
4189 // custom powi() function than the generic pow().
4190 if (args[1]->IsSmi()) {
4191 int y = Smi::cast(args[1])->value();
4192 return Heap::AllocateHeapNumber(powi(x, y));
4193 }
4194
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004195 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004196
4197 if (!isinf(x)) {
4198 if (y == 0.5) {
4199 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4200 // square root of a number. To speed up such computations, we
4201 // explictly check for this case and use the sqrt() function
4202 // which is faster than pow().
4203 return Heap::AllocateHeapNumber(sqrt(x));
4204 } else if (y == -0.5) {
4205 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4206 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4207 }
4208 }
4209
4210 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004211 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004212 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4213 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004214 } else {
4215 return Heap::AllocateHeapNumber(pow(x, y));
4216 }
4217}
4218
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004219
4220static Object* Runtime_Math_round(Arguments args) {
4221 NoHandleAllocation ha;
4222 ASSERT(args.length() == 1);
4223
4224 CONVERT_DOUBLE_CHECKED(x, args[0]);
4225 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4226 return Heap::NumberFromDouble(floor(x + 0.5));
4227}
4228
4229
4230static Object* Runtime_Math_sin(Arguments args) {
4231 NoHandleAllocation ha;
4232 ASSERT(args.length() == 1);
4233
4234 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004235 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004236}
4237
4238
4239static Object* Runtime_Math_sqrt(Arguments args) {
4240 NoHandleAllocation ha;
4241 ASSERT(args.length() == 1);
4242
4243 CONVERT_DOUBLE_CHECKED(x, args[0]);
4244 return Heap::AllocateHeapNumber(sqrt(x));
4245}
4246
4247
4248static Object* Runtime_Math_tan(Arguments args) {
4249 NoHandleAllocation ha;
4250 ASSERT(args.length() == 1);
4251
4252 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004253 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004254}
4255
4256
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004257// The NewArguments function is only used when constructing the
4258// arguments array when calling non-functions from JavaScript in
4259// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004260static Object* Runtime_NewArguments(Arguments args) {
4261 NoHandleAllocation ha;
4262 ASSERT(args.length() == 1);
4263
4264 // ECMA-262, 3rd., 10.1.8, p.39
4265 CONVERT_CHECKED(JSFunction, callee, args[0]);
4266
4267 // Compute the frame holding the arguments.
4268 JavaScriptFrameIterator it;
4269 it.AdvanceToArgumentsFrame();
4270 JavaScriptFrame* frame = it.frame();
4271
4272 const int length = frame->GetProvidedParametersCount();
4273 Object* result = Heap::AllocateArgumentsObject(callee, length);
4274 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004275 if (length > 0) {
4276 Object* obj = Heap::AllocateFixedArray(length);
4277 if (obj->IsFailure()) return obj;
4278 FixedArray* array = FixedArray::cast(obj);
4279 ASSERT(array->length() == length);
4280 WriteBarrierMode mode = array->GetWriteBarrierMode();
4281 for (int i = 0; i < length; i++) {
4282 array->set(i, frame->GetParameter(i), mode);
4283 }
4284 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004285 }
4286 return result;
4287}
4288
4289
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004290static Object* Runtime_NewArgumentsFast(Arguments args) {
4291 NoHandleAllocation ha;
4292 ASSERT(args.length() == 3);
4293
4294 JSFunction* callee = JSFunction::cast(args[0]);
4295 Object** parameters = reinterpret_cast<Object**>(args[1]);
4296 const int length = Smi::cast(args[2])->value();
4297
4298 Object* result = Heap::AllocateArgumentsObject(callee, length);
4299 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004300 ASSERT(Heap::InNewSpace(result));
4301
4302 // Allocate the elements if needed.
4303 if (length > 0) {
4304 // Allocate the fixed array.
4305 Object* obj = Heap::AllocateRawFixedArray(length);
4306 if (obj->IsFailure()) return obj;
4307 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4308 FixedArray* array = FixedArray::cast(obj);
4309 array->set_length(length);
4310 WriteBarrierMode mode = array->GetWriteBarrierMode();
4311 for (int i = 0; i < length; i++) {
4312 array->set(i, *--parameters, mode);
4313 }
4314 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4315 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004316 }
4317 return result;
4318}
4319
4320
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004321static Object* Runtime_NewClosure(Arguments args) {
4322 HandleScope scope;
4323 ASSERT(args.length() == 2);
4324 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4325 CONVERT_ARG_CHECKED(Context, context, 1);
4326
4327 Handle<JSFunction> result =
4328 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4329 return *result;
4330}
4331
4332
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004333static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004334 // TODO(385): Change this to create a construct stub specialized for
4335 // the given map to make allocation of simple objects - and maybe
4336 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004337 if (FLAG_inline_new
4338 && shared->has_only_simple_this_property_assignments()) {
4339 ConstructStubCompiler compiler;
4340 Object* code = compiler.CompileConstructStub(*shared);
4341 if (code->IsFailure()) {
4342 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4343 }
4344 return Code::cast(code);
4345 }
4346
4347 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004348}
4349
4350
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004351static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004352 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004353 ASSERT(args.length() == 1);
4354
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004355 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004356
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004357 // If the constructor isn't a proper function we throw a type error.
4358 if (!constructor->IsJSFunction()) {
4359 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4360 Handle<Object> type_error =
4361 Factory::NewTypeError("not_constructor", arguments);
4362 return Top::Throw(*type_error);
4363 }
4364
4365 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004366#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004367 // Handle stepping into constructors if step into is active.
4368 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004369 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004370 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004371#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004372
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004373 if (function->has_initial_map()) {
4374 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004375 // The 'Function' function ignores the receiver object when
4376 // called using 'new' and creates a new JSFunction object that
4377 // is returned. The receiver object is only used for error
4378 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004379 // JSFunction. Factory::NewJSObject() should not be used to
4380 // allocate JSFunctions since it does not properly initialize
4381 // the shared part of the function. Since the receiver is
4382 // ignored anyway, we use the global object as the receiver
4383 // instead of a new JSFunction object. This way, errors are
4384 // reported the same way whether or not 'Function' is called
4385 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004386 return Top::context()->global();
4387 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004388 }
4389
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004390 // The function should be compiled for the optimization hints to be available.
4391 if (!function->shared()->is_compiled()) {
4392 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4393 CLEAR_EXCEPTION,
4394 0);
4395 }
4396
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004397 bool first_allocation = !function->has_initial_map();
4398 Handle<JSObject> result = Factory::NewJSObject(function);
4399 if (first_allocation) {
4400 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004401 Handle<Code> stub = Handle<Code>(
4402 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004403 function->shared()->set_construct_stub(*stub);
4404 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004405
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004406 Counters::constructed_objects.Increment();
4407 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004408
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004409 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410}
4411
4412
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004413static Object* Runtime_LazyCompile(Arguments args) {
4414 HandleScope scope;
4415 ASSERT(args.length() == 1);
4416
4417 Handle<JSFunction> function = args.at<JSFunction>(0);
4418#ifdef DEBUG
4419 if (FLAG_trace_lazy) {
4420 PrintF("[lazy: ");
4421 function->shared()->name()->Print();
4422 PrintF("]\n");
4423 }
4424#endif
4425
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004426 // Compile the target function. Here we compile using CompileLazyInLoop in
4427 // order to get the optimized version. This helps code like delta-blue
4428 // that calls performance-critical routines through constructors. A
4429 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4430 // direct call. Since the in-loop tracking takes place through CallICs
4431 // this means that things called through constructors are never known to
4432 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004433 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004434 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004435 return Failure::Exception();
4436 }
4437
4438 return function->code();
4439}
4440
4441
4442static Object* Runtime_GetCalledFunction(Arguments args) {
4443 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004444 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004445 StackFrameIterator it;
4446 // Get past the JS-to-C exit frame.
4447 ASSERT(it.frame()->is_exit());
4448 it.Advance();
4449 // Get past the CALL_NON_FUNCTION activation frame.
4450 ASSERT(it.frame()->is_java_script());
4451 it.Advance();
4452 // Argument adaptor frames do not copy the function; we have to skip
4453 // past them to get to the real calling frame.
4454 if (it.frame()->is_arguments_adaptor()) it.Advance();
4455 // Get the function from the top of the expression stack of the
4456 // calling frame.
4457 StandardFrame* frame = StandardFrame::cast(it.frame());
4458 int index = frame->ComputeExpressionsCount() - 1;
4459 Object* result = frame->GetExpression(index);
4460 return result;
4461}
4462
4463
4464static Object* Runtime_GetFunctionDelegate(Arguments args) {
4465 HandleScope scope;
4466 ASSERT(args.length() == 1);
4467 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4468 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4469}
4470
4471
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004472static Object* Runtime_GetConstructorDelegate(Arguments args) {
4473 HandleScope scope;
4474 ASSERT(args.length() == 1);
4475 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4476 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4477}
4478
4479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004480static Object* Runtime_NewContext(Arguments args) {
4481 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004482 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004483
kasper.lund7276f142008-07-30 08:49:36 +00004484 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004485 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4486 Object* result = Heap::AllocateFunctionContext(length, function);
4487 if (result->IsFailure()) return result;
4488
4489 Top::set_context(Context::cast(result));
4490
kasper.lund7276f142008-07-30 08:49:36 +00004491 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004492}
4493
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004494static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004495 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004496 Object* js_object = object;
4497 if (!js_object->IsJSObject()) {
4498 js_object = js_object->ToObject();
4499 if (js_object->IsFailure()) {
4500 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004501 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004502 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004503 Handle<Object> result =
4504 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4505 return Top::Throw(*result);
4506 }
4507 }
4508
4509 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004510 Heap::AllocateWithContext(Top::context(),
4511 JSObject::cast(js_object),
4512 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004513 if (result->IsFailure()) return result;
4514
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004515 Context* context = Context::cast(result);
4516 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004517
kasper.lund7276f142008-07-30 08:49:36 +00004518 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004519}
4520
4521
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004522static Object* Runtime_PushContext(Arguments args) {
4523 NoHandleAllocation ha;
4524 ASSERT(args.length() == 1);
4525 return PushContextHelper(args[0], false);
4526}
4527
4528
4529static Object* Runtime_PushCatchContext(Arguments args) {
4530 NoHandleAllocation ha;
4531 ASSERT(args.length() == 1);
4532 return PushContextHelper(args[0], true);
4533}
4534
4535
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004536static Object* Runtime_LookupContext(Arguments args) {
4537 HandleScope scope;
4538 ASSERT(args.length() == 2);
4539
4540 CONVERT_ARG_CHECKED(Context, context, 0);
4541 CONVERT_ARG_CHECKED(String, name, 1);
4542
4543 int index;
4544 PropertyAttributes attributes;
4545 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004546 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004547 context->Lookup(name, flags, &index, &attributes);
4548
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004549 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004550 ASSERT(holder->IsJSObject());
4551 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004552 }
4553
4554 // No intermediate context found. Use global object by default.
4555 return Top::context()->global();
4556}
4557
4558
ager@chromium.orga1645e22009-09-09 19:27:10 +00004559// A mechanism to return a pair of Object pointers in registers (if possible).
4560// How this is achieved is calling convention-dependent.
4561// All currently supported x86 compiles uses calling conventions that are cdecl
4562// variants where a 64-bit value is returned in two 32-bit registers
4563// (edx:eax on ia32, r1:r0 on ARM).
4564// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4565// In Win64 calling convention, a struct of two pointers is returned in memory,
4566// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004567#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004568struct ObjectPair {
4569 Object* x;
4570 Object* y;
4571};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004572
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004573static inline ObjectPair MakePair(Object* x, Object* y) {
4574 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004575 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4576 // In Win64 they are assigned to a hidden first argument.
4577 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004578}
4579#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004580typedef uint64_t ObjectPair;
4581static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004582 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004583 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004584}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004585#endif
4586
4587
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004588static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004589 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4590 USE(attributes);
4591 return x->IsTheHole() ? Heap::undefined_value() : x;
4592}
4593
4594
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004595static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4596 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004597 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004598 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004599 JSFunction* context_extension_function =
4600 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004601 // If the holder isn't a context extension object, we just return it
4602 // as the receiver. This allows arguments objects to be used as
4603 // receivers, but only if they are put in the context scope chain
4604 // explicitly via a with-statement.
4605 Object* constructor = holder->map()->constructor();
4606 if (constructor != context_extension_function) return holder;
4607 // Fall back to using the global object as the receiver if the
4608 // property turns out to be a local variable allocated in a context
4609 // extension object - introduced via eval.
4610 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004611}
4612
4613
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004614static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004615 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004616 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004617
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004618 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004619 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004620 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004621 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004622 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004623
4624 int index;
4625 PropertyAttributes attributes;
4626 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004627 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004628 context->Lookup(name, flags, &index, &attributes);
4629
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004630 // If the index is non-negative, the slot has been found in a local
4631 // variable or a parameter. Read it from the context object or the
4632 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004633 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004634 // If the "property" we were looking for is a local variable or an
4635 // argument in a context, the receiver is the global object; see
4636 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4637 JSObject* receiver = Top::context()->global()->global_receiver();
4638 Object* value = (holder->IsContext())
4639 ? Context::cast(*holder)->get(index)
4640 : JSObject::cast(*holder)->GetElement(index);
4641 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004642 }
4643
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004644 // If the holder is found, we read the property from it.
4645 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004646 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004647 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004648 JSObject* receiver;
4649 if (object->IsGlobalObject()) {
4650 receiver = GlobalObject::cast(object)->global_receiver();
4651 } else if (context->is_exception_holder(*holder)) {
4652 receiver = Top::context()->global()->global_receiver();
4653 } else {
4654 receiver = ComputeReceiverForNonGlobal(object);
4655 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004656 // No need to unhole the value here. This is taken care of by the
4657 // GetProperty function.
4658 Object* value = object->GetProperty(*name);
4659 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004660 }
4661
4662 if (throw_error) {
4663 // The property doesn't exist - throw exception.
4664 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004665 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666 return MakePair(Top::Throw(*reference_error), NULL);
4667 } else {
4668 // The property doesn't exist - return undefined
4669 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4670 }
4671}
4672
4673
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004674static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004675 return LoadContextSlotHelper(args, true);
4676}
4677
4678
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004679static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004680 return LoadContextSlotHelper(args, false);
4681}
4682
4683
4684static Object* Runtime_StoreContextSlot(Arguments args) {
4685 HandleScope scope;
4686 ASSERT(args.length() == 3);
4687
4688 Handle<Object> value(args[0]);
4689 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004690 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691
4692 int index;
4693 PropertyAttributes attributes;
4694 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004695 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004696 context->Lookup(name, flags, &index, &attributes);
4697
4698 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004699 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004700 // Ignore if read_only variable.
4701 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004702 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004703 }
4704 } else {
4705 ASSERT((attributes & READ_ONLY) == 0);
4706 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004707 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004708 USE(result);
4709 ASSERT(!result->IsFailure());
4710 }
4711 return *value;
4712 }
4713
4714 // Slow case: The property is not in a FixedArray context.
4715 // It is either in an JSObject extension context or it was not found.
4716 Handle<JSObject> context_ext;
4717
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004718 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004719 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004720 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004721 } else {
4722 // The property was not found. It needs to be stored in the global context.
4723 ASSERT(attributes == ABSENT);
4724 attributes = NONE;
4725 context_ext = Handle<JSObject>(Top::context()->global());
4726 }
4727
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004728 // Set the property, but ignore if read_only variable on the context
4729 // extension object itself.
4730 if ((attributes & READ_ONLY) == 0 ||
4731 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004732 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4733 if (set.is_null()) {
4734 // Failure::Exception is converted to a null handle in the
4735 // handle-based methods such as SetProperty. We therefore need
4736 // to convert null handles back to exceptions.
4737 ASSERT(Top::has_pending_exception());
4738 return Failure::Exception();
4739 }
4740 }
4741 return *value;
4742}
4743
4744
4745static Object* Runtime_Throw(Arguments args) {
4746 HandleScope scope;
4747 ASSERT(args.length() == 1);
4748
4749 return Top::Throw(args[0]);
4750}
4751
4752
4753static Object* Runtime_ReThrow(Arguments args) {
4754 HandleScope scope;
4755 ASSERT(args.length() == 1);
4756
4757 return Top::ReThrow(args[0]);
4758}
4759
4760
4761static Object* Runtime_ThrowReferenceError(Arguments args) {
4762 HandleScope scope;
4763 ASSERT(args.length() == 1);
4764
4765 Handle<Object> name(args[0]);
4766 Handle<Object> reference_error =
4767 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4768 return Top::Throw(*reference_error);
4769}
4770
4771
4772static Object* Runtime_StackOverflow(Arguments args) {
4773 NoHandleAllocation na;
4774 return Top::StackOverflow();
4775}
4776
4777
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004778static Object* Runtime_StackGuard(Arguments args) {
4779 ASSERT(args.length() == 1);
4780
4781 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004782 if (StackGuard::IsStackOverflow()) {
4783 return Runtime_StackOverflow(args);
4784 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004785
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004786 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004787}
4788
4789
4790// NOTE: These PrintXXX functions are defined for all builds (not just
4791// DEBUG builds) because we may want to be able to trace function
4792// calls in all modes.
4793static void PrintString(String* str) {
4794 // not uncommon to have empty strings
4795 if (str->length() > 0) {
4796 SmartPointer<char> s =
4797 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4798 PrintF("%s", *s);
4799 }
4800}
4801
4802
4803static void PrintObject(Object* obj) {
4804 if (obj->IsSmi()) {
4805 PrintF("%d", Smi::cast(obj)->value());
4806 } else if (obj->IsString() || obj->IsSymbol()) {
4807 PrintString(String::cast(obj));
4808 } else if (obj->IsNumber()) {
4809 PrintF("%g", obj->Number());
4810 } else if (obj->IsFailure()) {
4811 PrintF("<failure>");
4812 } else if (obj->IsUndefined()) {
4813 PrintF("<undefined>");
4814 } else if (obj->IsNull()) {
4815 PrintF("<null>");
4816 } else if (obj->IsTrue()) {
4817 PrintF("<true>");
4818 } else if (obj->IsFalse()) {
4819 PrintF("<false>");
4820 } else {
4821 PrintF("%p", obj);
4822 }
4823}
4824
4825
4826static int StackSize() {
4827 int n = 0;
4828 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4829 return n;
4830}
4831
4832
4833static void PrintTransition(Object* result) {
4834 // indentation
4835 { const int nmax = 80;
4836 int n = StackSize();
4837 if (n <= nmax)
4838 PrintF("%4d:%*s", n, n, "");
4839 else
4840 PrintF("%4d:%*s", n, nmax, "...");
4841 }
4842
4843 if (result == NULL) {
4844 // constructor calls
4845 JavaScriptFrameIterator it;
4846 JavaScriptFrame* frame = it.frame();
4847 if (frame->IsConstructor()) PrintF("new ");
4848 // function name
4849 Object* fun = frame->function();
4850 if (fun->IsJSFunction()) {
4851 PrintObject(JSFunction::cast(fun)->shared()->name());
4852 } else {
4853 PrintObject(fun);
4854 }
4855 // function arguments
4856 // (we are intentionally only printing the actually
4857 // supplied parameters, not all parameters required)
4858 PrintF("(this=");
4859 PrintObject(frame->receiver());
4860 const int length = frame->GetProvidedParametersCount();
4861 for (int i = 0; i < length; i++) {
4862 PrintF(", ");
4863 PrintObject(frame->GetParameter(i));
4864 }
4865 PrintF(") {\n");
4866
4867 } else {
4868 // function result
4869 PrintF("} -> ");
4870 PrintObject(result);
4871 PrintF("\n");
4872 }
4873}
4874
4875
4876static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004877 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878 NoHandleAllocation ha;
4879 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004880 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004881}
4882
4883
4884static Object* Runtime_TraceExit(Arguments args) {
4885 NoHandleAllocation ha;
4886 PrintTransition(args[0]);
4887 return args[0]; // return TOS
4888}
4889
4890
4891static Object* Runtime_DebugPrint(Arguments args) {
4892 NoHandleAllocation ha;
4893 ASSERT(args.length() == 1);
4894
4895#ifdef DEBUG
4896 if (args[0]->IsString()) {
4897 // If we have a string, assume it's a code "marker"
4898 // and print some interesting cpu debugging info.
4899 JavaScriptFrameIterator it;
4900 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004901 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4902 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004903 } else {
4904 PrintF("DebugPrint: ");
4905 }
4906 args[0]->Print();
4907#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004908 // ShortPrint is available in release mode. Print is not.
4909 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004910#endif
4911 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004912 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004913
4914 return args[0]; // return TOS
4915}
4916
4917
4918static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004919 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004920 NoHandleAllocation ha;
4921 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004922 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004923}
4924
4925
mads.s.ager31e71382008-08-13 09:32:07 +00004926static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004927 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004928 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004929
4930 // According to ECMA-262, section 15.9.1, page 117, the precision of
4931 // the number in a Date object representing a particular instant in
4932 // time is milliseconds. Therefore, we floor the result of getting
4933 // the OS time.
4934 double millis = floor(OS::TimeCurrentMillis());
4935 return Heap::NumberFromDouble(millis);
4936}
4937
4938
4939static Object* Runtime_DateParseString(Arguments args) {
4940 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004941 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004942
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004943 CONVERT_ARG_CHECKED(String, str, 0);
4944 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004945
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004946 CONVERT_ARG_CHECKED(JSArray, output, 1);
4947 RUNTIME_ASSERT(output->HasFastElements());
4948
4949 AssertNoAllocation no_allocation;
4950
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004951 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004952 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4953 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004954 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004955 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004956 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004957 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004958 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4959 }
4960
4961 if (result) {
4962 return *output;
4963 } else {
4964 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004965 }
4966}
4967
4968
4969static Object* Runtime_DateLocalTimezone(Arguments args) {
4970 NoHandleAllocation ha;
4971 ASSERT(args.length() == 1);
4972
4973 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004974 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004975 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4976}
4977
4978
4979static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4980 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004981 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004982
4983 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4984}
4985
4986
4987static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4988 NoHandleAllocation ha;
4989 ASSERT(args.length() == 1);
4990
4991 CONVERT_DOUBLE_CHECKED(x, args[0]);
4992 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4993}
4994
4995
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004996static Object* Runtime_NumberIsFinite(Arguments args) {
4997 NoHandleAllocation ha;
4998 ASSERT(args.length() == 1);
4999
5000 CONVERT_DOUBLE_CHECKED(value, args[0]);
5001 Object* result;
5002 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5003 result = Heap::false_value();
5004 } else {
5005 result = Heap::true_value();
5006 }
5007 return result;
5008}
5009
5010
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005011static Object* Runtime_GlobalReceiver(Arguments args) {
5012 ASSERT(args.length() == 1);
5013 Object* global = args[0];
5014 if (!global->IsJSGlobalObject()) return Heap::null_value();
5015 return JSGlobalObject::cast(global)->global_receiver();
5016}
5017
5018
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005019static Object* Runtime_CompileString(Arguments args) {
5020 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005021 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005022 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005023 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005024
ager@chromium.org381abbb2009-02-25 13:23:22 +00005025 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005026 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005027 Compiler::ValidationState validate = (is_json->IsTrue())
5028 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005029 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5030 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005031 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005032 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005033 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005034 Handle<JSFunction> fun =
5035 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5036 return *fun;
5037}
5038
5039
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005040static Handle<JSFunction> GetBuiltinFunction(String* name) {
5041 LookupResult result;
5042 Top::global_context()->builtins()->LocalLookup(name, &result);
5043 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5044}
5045
5046
5047static Object* CompileDirectEval(Handle<String> source) {
5048 // Compute the eval context.
5049 HandleScope scope;
5050 StackFrameLocator locator;
5051 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5052 Handle<Context> context(Context::cast(frame->context()));
5053 bool is_global = context->IsGlobalContext();
5054
ager@chromium.org381abbb2009-02-25 13:23:22 +00005055 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005056 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5057 source,
5058 context,
5059 is_global,
5060 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005061 if (boilerplate.is_null()) return Failure::Exception();
5062 Handle<JSFunction> fun =
5063 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5064 return *fun;
5065}
5066
5067
5068static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5069 ASSERT(args.length() == 2);
5070
5071 HandleScope scope;
5072
5073 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5074
5075 Handle<Object> receiver;
5076
5077 // Find where the 'eval' symbol is bound. It is unaliased only if
5078 // it is bound in the global context.
5079 StackFrameLocator locator;
5080 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5081 Handle<Context> context(Context::cast(frame->context()));
5082 int index;
5083 PropertyAttributes attributes;
5084 while (!context.is_null()) {
5085 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5086 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005087 // Stop search when eval is found or when the global context is
5088 // reached.
5089 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005090 if (context->is_function_context()) {
5091 context = Handle<Context>(Context::cast(context->closure()->context()));
5092 } else {
5093 context = Handle<Context>(context->previous());
5094 }
5095 }
5096
iposva@chromium.org245aa852009-02-10 00:49:54 +00005097 // If eval could not be resolved, it has been deleted and we need to
5098 // throw a reference error.
5099 if (attributes == ABSENT) {
5100 Handle<Object> name = Factory::eval_symbol();
5101 Handle<Object> reference_error =
5102 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5103 return Top::Throw(*reference_error);
5104 }
5105
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005106 if (context->IsGlobalContext()) {
5107 // 'eval' is bound in the global context, but it may have been overwritten.
5108 // Compare it to the builtin 'GlobalEval' function to make sure.
5109 Handle<JSFunction> global_eval =
5110 GetBuiltinFunction(Heap::global_eval_symbol());
5111 if (global_eval.is_identical_to(callee)) {
5112 // A direct eval call.
5113 if (args[1]->IsString()) {
5114 CONVERT_ARG_CHECKED(String, source, 1);
5115 // A normal eval call on a string. Compile it and return the
5116 // compiled function bound in the local context.
5117 Object* compiled_source = CompileDirectEval(source);
5118 if (compiled_source->IsFailure()) return compiled_source;
5119 receiver = Handle<Object>(frame->receiver());
5120 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5121 } else {
5122 // An eval call that is not called on a string. Global eval
5123 // deals better with this.
5124 receiver = Handle<Object>(Top::global_context()->global());
5125 }
5126 } else {
5127 // 'eval' is overwritten. Just call the function with the given arguments.
5128 receiver = Handle<Object>(Top::global_context()->global());
5129 }
5130 } else {
5131 // 'eval' is not bound in the global context. Just call the function
5132 // with the given arguments. This is not necessarily the global eval.
5133 if (receiver->IsContext()) {
5134 context = Handle<Context>::cast(receiver);
5135 receiver = Handle<Object>(context->get(index));
5136 }
5137 }
5138
5139 Handle<FixedArray> call = Factory::NewFixedArray(2);
5140 call->set(0, *callee);
5141 call->set(1, *receiver);
5142 return *call;
5143}
5144
5145
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005146static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5147 // This utility adjusts the property attributes for newly created Function
5148 // object ("new Function(...)") by changing the map.
5149 // All it does is changing the prototype property to enumerable
5150 // as specified in ECMA262, 15.3.5.2.
5151 HandleScope scope;
5152 ASSERT(args.length() == 1);
5153 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5154 ASSERT(func->map()->instance_type() ==
5155 Top::function_instance_map()->instance_type());
5156 ASSERT(func->map()->instance_size() ==
5157 Top::function_instance_map()->instance_size());
5158 func->set_map(*Top::function_instance_map());
5159 return *func;
5160}
5161
5162
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005163// Push an array unto an array of arrays if it is not already in the
5164// array. Returns true if the element was pushed on the stack and
5165// false otherwise.
5166static Object* Runtime_PushIfAbsent(Arguments args) {
5167 ASSERT(args.length() == 2);
5168 CONVERT_CHECKED(JSArray, array, args[0]);
5169 CONVERT_CHECKED(JSArray, element, args[1]);
5170 RUNTIME_ASSERT(array->HasFastElements());
5171 int length = Smi::cast(array->length())->value();
5172 FixedArray* elements = FixedArray::cast(array->elements());
5173 for (int i = 0; i < length; i++) {
5174 if (elements->get(i) == element) return Heap::false_value();
5175 }
5176 Object* obj = array->SetFastElement(length, element);
5177 if (obj->IsFailure()) return obj;
5178 return Heap::true_value();
5179}
5180
5181
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005182/**
5183 * A simple visitor visits every element of Array's.
5184 * The backend storage can be a fixed array for fast elements case,
5185 * or a dictionary for sparse array. Since Dictionary is a subtype
5186 * of FixedArray, the class can be used by both fast and slow cases.
5187 * The second parameter of the constructor, fast_elements, specifies
5188 * whether the storage is a FixedArray or Dictionary.
5189 *
5190 * An index limit is used to deal with the situation that a result array
5191 * length overflows 32-bit non-negative integer.
5192 */
5193class ArrayConcatVisitor {
5194 public:
5195 ArrayConcatVisitor(Handle<FixedArray> storage,
5196 uint32_t index_limit,
5197 bool fast_elements) :
5198 storage_(storage), index_limit_(index_limit),
5199 fast_elements_(fast_elements), index_offset_(0) { }
5200
5201 void visit(uint32_t i, Handle<Object> elm) {
5202 uint32_t index = i + index_offset_;
5203 if (index >= index_limit_) return;
5204
5205 if (fast_elements_) {
5206 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5207 storage_->set(index, *elm);
5208
5209 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005210 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5211 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005212 Factory::DictionaryAtNumberPut(dict, index, elm);
5213 if (!result.is_identical_to(dict))
5214 storage_ = result;
5215 }
5216 }
5217
5218 void increase_index_offset(uint32_t delta) {
5219 index_offset_ += delta;
5220 }
5221
5222 private:
5223 Handle<FixedArray> storage_;
5224 uint32_t index_limit_;
5225 bool fast_elements_;
5226 uint32_t index_offset_;
5227};
5228
5229
5230/**
5231 * A helper function that visits elements of a JSObject. Only elements
5232 * whose index between 0 and range (exclusive) are visited.
5233 *
5234 * If the third parameter, visitor, is not NULL, the visitor is called
5235 * with parameters, 'visitor_index_offset + element index' and the element.
5236 *
5237 * It returns the number of visisted elements.
5238 */
5239static uint32_t IterateElements(Handle<JSObject> receiver,
5240 uint32_t range,
5241 ArrayConcatVisitor* visitor) {
5242 uint32_t num_of_elements = 0;
5243
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005244 switch (receiver->GetElementsKind()) {
5245 case JSObject::FAST_ELEMENTS: {
5246 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5247 uint32_t len = elements->length();
5248 if (range < len) {
5249 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005250 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005251
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005252 for (uint32_t j = 0; j < len; j++) {
5253 Handle<Object> e(elements->get(j));
5254 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005255 num_of_elements++;
5256 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005257 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005258 }
5259 }
5260 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005261 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005262 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005263 case JSObject::PIXEL_ELEMENTS: {
5264 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5265 uint32_t len = pixels->length();
5266 if (range < len) {
5267 len = range;
5268 }
5269
5270 for (uint32_t j = 0; j < len; j++) {
5271 num_of_elements++;
5272 if (visitor != NULL) {
5273 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5274 visitor->visit(j, e);
5275 }
5276 }
5277 break;
5278 }
5279 case JSObject::DICTIONARY_ELEMENTS: {
5280 Handle<NumberDictionary> dict(receiver->element_dictionary());
5281 uint32_t capacity = dict->Capacity();
5282 for (uint32_t j = 0; j < capacity; j++) {
5283 Handle<Object> k(dict->KeyAt(j));
5284 if (dict->IsKey(*k)) {
5285 ASSERT(k->IsNumber());
5286 uint32_t index = static_cast<uint32_t>(k->Number());
5287 if (index < range) {
5288 num_of_elements++;
5289 if (visitor) {
5290 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5291 }
5292 }
5293 }
5294 }
5295 break;
5296 }
5297 default:
5298 UNREACHABLE();
5299 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005300 }
5301
5302 return num_of_elements;
5303}
5304
5305
5306/**
5307 * A helper function that visits elements of an Array object, and elements
5308 * on its prototypes.
5309 *
5310 * Elements on prototypes are visited first, and only elements whose indices
5311 * less than Array length are visited.
5312 *
5313 * If a ArrayConcatVisitor object is given, the visitor is called with
5314 * parameters, element's index + visitor_index_offset and the element.
5315 */
5316static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5317 ArrayConcatVisitor* visitor) {
5318 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5319 Handle<Object> obj = array;
5320
5321 static const int kEstimatedPrototypes = 3;
5322 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5323
5324 // Visit prototype first. If an element on the prototype is shadowed by
5325 // the inheritor using the same index, the ArrayConcatVisitor visits
5326 // the prototype element before the shadowing element.
5327 // The visitor can simply overwrite the old value by new value using
5328 // the same index. This follows Array::concat semantics.
5329 while (!obj->IsNull()) {
5330 objects.Add(Handle<JSObject>::cast(obj));
5331 obj = Handle<Object>(obj->GetPrototype());
5332 }
5333
5334 uint32_t nof_elements = 0;
5335 for (int i = objects.length() - 1; i >= 0; i--) {
5336 Handle<JSObject> obj = objects[i];
5337 nof_elements +=
5338 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5339 }
5340
5341 return nof_elements;
5342}
5343
5344
5345/**
5346 * A helper function of Runtime_ArrayConcat.
5347 *
5348 * The first argument is an Array of arrays and objects. It is the
5349 * same as the arguments array of Array::concat JS function.
5350 *
5351 * If an argument is an Array object, the function visits array
5352 * elements. If an argument is not an Array object, the function
5353 * visits the object as if it is an one-element array.
5354 *
5355 * If the result array index overflows 32-bit integer, the rounded
5356 * non-negative number is used as new length. For example, if one
5357 * array length is 2^32 - 1, second array length is 1, the
5358 * concatenated array length is 0.
5359 */
5360static uint32_t IterateArguments(Handle<JSArray> arguments,
5361 ArrayConcatVisitor* visitor) {
5362 uint32_t visited_elements = 0;
5363 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5364
5365 for (uint32_t i = 0; i < num_of_args; i++) {
5366 Handle<Object> obj(arguments->GetElement(i));
5367 if (obj->IsJSArray()) {
5368 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5369 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5370 uint32_t nof_elements =
5371 IterateArrayAndPrototypeElements(array, visitor);
5372 // Total elements of array and its prototype chain can be more than
5373 // the array length, but ArrayConcat can only concatenate at most
5374 // the array length number of elements.
5375 visited_elements += (nof_elements > len) ? len : nof_elements;
5376 if (visitor) visitor->increase_index_offset(len);
5377
5378 } else {
5379 if (visitor) {
5380 visitor->visit(0, obj);
5381 visitor->increase_index_offset(1);
5382 }
5383 visited_elements++;
5384 }
5385 }
5386 return visited_elements;
5387}
5388
5389
5390/**
5391 * Array::concat implementation.
5392 * See ECMAScript 262, 15.4.4.4.
5393 */
5394static Object* Runtime_ArrayConcat(Arguments args) {
5395 ASSERT(args.length() == 1);
5396 HandleScope handle_scope;
5397
5398 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5399 Handle<JSArray> arguments(arg_arrays);
5400
5401 // Pass 1: estimate the number of elements of the result
5402 // (it could be more than real numbers if prototype has elements).
5403 uint32_t result_length = 0;
5404 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5405
5406 { AssertNoAllocation nogc;
5407 for (uint32_t i = 0; i < num_of_args; i++) {
5408 Object* obj = arguments->GetElement(i);
5409 if (obj->IsJSArray()) {
5410 result_length +=
5411 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5412 } else {
5413 result_length++;
5414 }
5415 }
5416 }
5417
5418 // Allocate an empty array, will set length and content later.
5419 Handle<JSArray> result = Factory::NewJSArray(0);
5420
5421 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5422 // If estimated number of elements is more than half of length, a
5423 // fixed array (fast case) is more time and space-efficient than a
5424 // dictionary.
5425 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5426
5427 Handle<FixedArray> storage;
5428 if (fast_case) {
5429 // The backing storage array must have non-existing elements to
5430 // preserve holes across concat operations.
5431 storage = Factory::NewFixedArrayWithHoles(result_length);
5432
5433 } else {
5434 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5435 uint32_t at_least_space_for = estimate_nof_elements +
5436 (estimate_nof_elements >> 2);
5437 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005438 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005439 }
5440
5441 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5442
5443 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5444
5445 IterateArguments(arguments, &visitor);
5446
5447 result->set_length(*len);
5448 result->set_elements(*storage);
5449
5450 return *result;
5451}
5452
5453
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005454// This will not allocate (flatten the string), but it may run
5455// very slowly for very deeply nested ConsStrings. For debugging use only.
5456static Object* Runtime_GlobalPrint(Arguments args) {
5457 NoHandleAllocation ha;
5458 ASSERT(args.length() == 1);
5459
5460 CONVERT_CHECKED(String, string, args[0]);
5461 StringInputBuffer buffer(string);
5462 while (buffer.has_more()) {
5463 uint16_t character = buffer.GetNext();
5464 PrintF("%c", character);
5465 }
5466 return string;
5467}
5468
ager@chromium.org5ec48922009-05-05 07:25:34 +00005469// Moves all own elements of an object, that are below a limit, to positions
5470// starting at zero. All undefined values are placed after non-undefined values,
5471// and are followed by non-existing element. Does not change the length
5472// property.
5473// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005474static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005475 ASSERT(args.length() == 2);
5476 CONVERT_CHECKED(JSObject, object, args[0]);
5477 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5478 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005479}
5480
5481
5482// Move contents of argument 0 (an array) to argument 1 (an array)
5483static Object* Runtime_MoveArrayContents(Arguments args) {
5484 ASSERT(args.length() == 2);
5485 CONVERT_CHECKED(JSArray, from, args[0]);
5486 CONVERT_CHECKED(JSArray, to, args[1]);
5487 to->SetContent(FixedArray::cast(from->elements()));
5488 to->set_length(from->length());
5489 from->SetContent(Heap::empty_fixed_array());
5490 from->set_length(0);
5491 return to;
5492}
5493
5494
5495// How many elements does this array have?
5496static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5497 ASSERT(args.length() == 1);
5498 CONVERT_CHECKED(JSArray, array, args[0]);
5499 HeapObject* elements = array->elements();
5500 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005501 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502 } else {
5503 return array->length();
5504 }
5505}
5506
5507
5508// Returns an array that tells you where in the [0, length) interval an array
5509// might have elements. Can either return keys or intervals. Keys can have
5510// gaps in (undefined). Intervals can also span over some undefined keys.
5511static Object* Runtime_GetArrayKeys(Arguments args) {
5512 ASSERT(args.length() == 2);
5513 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005514 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005515 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005516 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005517 // Create an array and get all the keys into it, then remove all the
5518 // keys that are not integers in the range 0 to length-1.
5519 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5520 int keys_length = keys->length();
5521 for (int i = 0; i < keys_length; i++) {
5522 Object* key = keys->get(i);
5523 uint32_t index;
5524 if (!Array::IndexFromObject(key, &index) || index >= length) {
5525 // Zap invalid keys.
5526 keys->set_undefined(i);
5527 }
5528 }
5529 return *Factory::NewJSArrayWithElements(keys);
5530 } else {
5531 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5532 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005533 single_interval->set(0,
5534 Smi::FromInt(-1),
5535 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005536 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5537 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005538 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005539 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 single_interval->set(1, *length_object);
5541 return *Factory::NewJSArrayWithElements(single_interval);
5542 }
5543}
5544
5545
5546// DefineAccessor takes an optional final argument which is the
5547// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5548// to the way accessors are implemented, it is set for both the getter
5549// and setter on the first call to DefineAccessor and ignored on
5550// subsequent calls.
5551static Object* Runtime_DefineAccessor(Arguments args) {
5552 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5553 // Compute attributes.
5554 PropertyAttributes attributes = NONE;
5555 if (args.length() == 5) {
5556 CONVERT_CHECKED(Smi, attrs, args[4]);
5557 int value = attrs->value();
5558 // Only attribute bits should be set.
5559 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5560 attributes = static_cast<PropertyAttributes>(value);
5561 }
5562
5563 CONVERT_CHECKED(JSObject, obj, args[0]);
5564 CONVERT_CHECKED(String, name, args[1]);
5565 CONVERT_CHECKED(Smi, flag, args[2]);
5566 CONVERT_CHECKED(JSFunction, fun, args[3]);
5567 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5568}
5569
5570
5571static Object* Runtime_LookupAccessor(Arguments args) {
5572 ASSERT(args.length() == 3);
5573 CONVERT_CHECKED(JSObject, obj, args[0]);
5574 CONVERT_CHECKED(String, name, args[1]);
5575 CONVERT_CHECKED(Smi, flag, args[2]);
5576 return obj->LookupAccessor(name, flag->value() == 0);
5577}
5578
5579
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005580#ifdef ENABLE_DEBUGGER_SUPPORT
5581static Object* Runtime_DebugBreak(Arguments args) {
5582 ASSERT(args.length() == 0);
5583 return Execution::DebugBreakHelper();
5584}
5585
5586
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005587// Helper functions for wrapping and unwrapping stack frame ids.
5588static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005589 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005590 return Smi::FromInt(id >> 2);
5591}
5592
5593
5594static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5595 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5596}
5597
5598
5599// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005600// args[0]: debug event listener function to set or null or undefined for
5601// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005602// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005603static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005604 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005605 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5606 args[0]->IsUndefined() ||
5607 args[0]->IsNull());
5608 Handle<Object> callback = args.at<Object>(0);
5609 Handle<Object> data = args.at<Object>(1);
5610 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005611
5612 return Heap::undefined_value();
5613}
5614
5615
5616static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005617 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005618 StackGuard::DebugBreak();
5619 return Heap::undefined_value();
5620}
5621
5622
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005623// Find the length of the prototype chain that is to to handled as one. If a
5624// prototype object is hidden it is to be viewed as part of the the object it
5625// is prototype for.
5626static int LocalPrototypeChainLength(JSObject* obj) {
5627 int count = 1;
5628 Object* proto = obj->GetPrototype();
5629 while (proto->IsJSObject() &&
5630 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5631 count++;
5632 proto = JSObject::cast(proto)->GetPrototype();
5633 }
5634 return count;
5635}
5636
5637
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005638static Object* DebugLookupResultValue(Object* receiver, String* name,
5639 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005640 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005641 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005642 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005643 case NORMAL:
5644 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005645 if (value->IsTheHole()) {
5646 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005647 }
5648 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005649 case FIELD:
5650 value =
5651 JSObject::cast(
5652 result->holder())->FastPropertyAt(result->GetFieldIndex());
5653 if (value->IsTheHole()) {
5654 return Heap::undefined_value();
5655 }
5656 return value;
5657 case CONSTANT_FUNCTION:
5658 return result->GetConstantFunction();
5659 case CALLBACKS: {
5660 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005661 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005662 value = receiver->GetPropertyWithCallback(
5663 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005664 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005665 value = Top::pending_exception();
5666 Top::clear_pending_exception();
5667 if (caught_exception != NULL) {
5668 *caught_exception = true;
5669 }
5670 }
5671 return value;
5672 } else {
5673 return Heap::undefined_value();
5674 }
5675 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005676 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005677 case MAP_TRANSITION:
5678 case CONSTANT_TRANSITION:
5679 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005680 return Heap::undefined_value();
5681 default:
5682 UNREACHABLE();
5683 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005684 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005685 return Heap::undefined_value();
5686}
5687
5688
ager@chromium.org32912102009-01-16 10:38:43 +00005689// Get debugger related details for an object property.
5690// args[0]: object holding property
5691// args[1]: name of the property
5692//
5693// The array returned contains the following information:
5694// 0: Property value
5695// 1: Property details
5696// 2: Property value is exception
5697// 3: Getter function if defined
5698// 4: Setter function if defined
5699// Items 2-4 are only filled if the property has either a getter or a setter
5700// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005701static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005702 HandleScope scope;
5703
5704 ASSERT(args.length() == 2);
5705
5706 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5707 CONVERT_ARG_CHECKED(String, name, 1);
5708
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005709 // Make sure to set the current context to the context before the debugger was
5710 // entered (if the debugger is entered). The reason for switching context here
5711 // is that for some property lookups (accessors and interceptors) callbacks
5712 // into the embedding application can occour, and the embedding application
5713 // could have the assumption that its own global context is the current
5714 // context and not some internal debugger context.
5715 SaveContext save;
5716 if (Debug::InDebugger()) {
5717 Top::set_context(*Debug::debugger_entry()->GetContext());
5718 }
5719
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005720 // Skip the global proxy as it has no properties and always delegates to the
5721 // real global object.
5722 if (obj->IsJSGlobalProxy()) {
5723 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5724 }
5725
5726
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005727 // Check if the name is trivially convertible to an index and get the element
5728 // if so.
5729 uint32_t index;
5730 if (name->AsArrayIndex(&index)) {
5731 Handle<FixedArray> details = Factory::NewFixedArray(2);
5732 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5733 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5734 return *Factory::NewJSArrayWithElements(details);
5735 }
5736
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005737 // Find the number of objects making up this.
5738 int length = LocalPrototypeChainLength(*obj);
5739
5740 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005741 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005742 Handle<JSObject> jsproto = obj;
5743 for (int i = 0; i < length; i++) {
5744 jsproto->LocalLookup(*name, &result);
5745 if (result.IsProperty()) {
5746 break;
5747 }
5748 if (i < length - 1) {
5749 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5750 }
5751 }
5752
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005753 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005754 // LookupResult is not GC safe as all its members are raw object pointers.
5755 // When calling DebugLookupResultValue GC can happen as this might invoke
5756 // callbacks. After the call to DebugLookupResultValue the callback object
5757 // in the LookupResult might still be needed. Put it into a handle for later
5758 // use.
5759 PropertyType result_type = result.type();
5760 Handle<Object> result_callback_obj;
5761 if (result_type == CALLBACKS) {
5762 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5763 }
5764
5765 // Find the actual value. Don't use result after this call as it's content
5766 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005767 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005768 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005769 &caught_exception);
5770 if (value->IsFailure()) return value;
5771 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005772
ager@chromium.org32912102009-01-16 10:38:43 +00005773 // If the callback object is a fixed array then it contains JavaScript
5774 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005775 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5776 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005777 Handle<FixedArray> details =
5778 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005779 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005780 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005781 if (hasJavaScriptAccessors) {
5782 details->set(2,
5783 caught_exception ? Heap::true_value() : Heap::false_value());
5784 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5785 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5786 }
5787
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005788 return *Factory::NewJSArrayWithElements(details);
5789 }
5790 return Heap::undefined_value();
5791}
5792
5793
5794static Object* Runtime_DebugGetProperty(Arguments args) {
5795 HandleScope scope;
5796
5797 ASSERT(args.length() == 2);
5798
5799 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5800 CONVERT_ARG_CHECKED(String, name, 1);
5801
5802 LookupResult result;
5803 obj->Lookup(*name, &result);
5804 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005805 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005806 }
5807 return Heap::undefined_value();
5808}
5809
5810
5811// Return the names of the local named properties.
5812// args[0]: object
5813static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5814 HandleScope scope;
5815 ASSERT(args.length() == 1);
5816 if (!args[0]->IsJSObject()) {
5817 return Heap::undefined_value();
5818 }
5819 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5820
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005821 // Skip the global proxy as it has no properties and always delegates to the
5822 // real global object.
5823 if (obj->IsJSGlobalProxy()) {
5824 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5825 }
5826
5827 // Find the number of objects making up this.
5828 int length = LocalPrototypeChainLength(*obj);
5829
5830 // Find the number of local properties for each of the objects.
5831 int* local_property_count = NewArray<int>(length);
5832 int total_property_count = 0;
5833 Handle<JSObject> jsproto = obj;
5834 for (int i = 0; i < length; i++) {
5835 int n;
5836 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5837 local_property_count[i] = n;
5838 total_property_count += n;
5839 if (i < length - 1) {
5840 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5841 }
5842 }
5843
5844 // Allocate an array with storage for all the property names.
5845 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5846
5847 // Get the property names.
5848 jsproto = obj;
5849 for (int i = 0; i < length; i++) {
5850 jsproto->GetLocalPropertyNames(*names,
5851 i == 0 ? 0 : local_property_count[i - 1]);
5852 if (i < length - 1) {
5853 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5854 }
5855 }
5856
5857 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005858 return *Factory::NewJSArrayWithElements(names);
5859}
5860
5861
5862// Return the names of the local indexed properties.
5863// args[0]: object
5864static Object* Runtime_DebugLocalElementNames(Arguments args) {
5865 HandleScope scope;
5866 ASSERT(args.length() == 1);
5867 if (!args[0]->IsJSObject()) {
5868 return Heap::undefined_value();
5869 }
5870 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5871
5872 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5873 Handle<FixedArray> names = Factory::NewFixedArray(n);
5874 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5875 return *Factory::NewJSArrayWithElements(names);
5876}
5877
5878
5879// Return the property type calculated from the property details.
5880// args[0]: smi with property details.
5881static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5882 ASSERT(args.length() == 1);
5883 CONVERT_CHECKED(Smi, details, args[0]);
5884 PropertyType type = PropertyDetails(details).type();
5885 return Smi::FromInt(static_cast<int>(type));
5886}
5887
5888
5889// Return the property attribute calculated from the property details.
5890// args[0]: smi with property details.
5891static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5892 ASSERT(args.length() == 1);
5893 CONVERT_CHECKED(Smi, details, args[0]);
5894 PropertyAttributes attributes = PropertyDetails(details).attributes();
5895 return Smi::FromInt(static_cast<int>(attributes));
5896}
5897
5898
5899// Return the property insertion index calculated from the property details.
5900// args[0]: smi with property details.
5901static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5902 ASSERT(args.length() == 1);
5903 CONVERT_CHECKED(Smi, details, args[0]);
5904 int index = PropertyDetails(details).index();
5905 return Smi::FromInt(index);
5906}
5907
5908
5909// Return information on whether an object has a named or indexed interceptor.
5910// args[0]: object
5911static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5912 HandleScope scope;
5913 ASSERT(args.length() == 1);
5914 if (!args[0]->IsJSObject()) {
5915 return Smi::FromInt(0);
5916 }
5917 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5918
5919 int result = 0;
5920 if (obj->HasNamedInterceptor()) result |= 2;
5921 if (obj->HasIndexedInterceptor()) result |= 1;
5922
5923 return Smi::FromInt(result);
5924}
5925
5926
5927// Return property names from named interceptor.
5928// args[0]: object
5929static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5930 HandleScope scope;
5931 ASSERT(args.length() == 1);
5932 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005933
ager@chromium.org32912102009-01-16 10:38:43 +00005934 if (obj->HasNamedInterceptor()) {
5935 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5936 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5937 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005938 return Heap::undefined_value();
5939}
5940
5941
5942// Return element names from indexed interceptor.
5943// args[0]: object
5944static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5945 HandleScope scope;
5946 ASSERT(args.length() == 1);
5947 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005948
ager@chromium.org32912102009-01-16 10:38:43 +00005949 if (obj->HasIndexedInterceptor()) {
5950 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5951 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5952 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005953 return Heap::undefined_value();
5954}
5955
5956
5957// Return property value from named interceptor.
5958// args[0]: object
5959// args[1]: property name
5960static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5961 HandleScope scope;
5962 ASSERT(args.length() == 2);
5963 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5964 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5965 CONVERT_ARG_CHECKED(String, name, 1);
5966
5967 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005968 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005969}
5970
5971
5972// Return element value from indexed interceptor.
5973// args[0]: object
5974// args[1]: index
5975static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5976 HandleScope scope;
5977 ASSERT(args.length() == 2);
5978 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5979 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5980 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5981
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005982 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005983}
5984
5985
5986static Object* Runtime_CheckExecutionState(Arguments args) {
5987 ASSERT(args.length() >= 1);
5988 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005989 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005990 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005991 return Top::Throw(Heap::illegal_execution_state_symbol());
5992 }
5993
5994 return Heap::true_value();
5995}
5996
5997
5998static Object* Runtime_GetFrameCount(Arguments args) {
5999 HandleScope scope;
6000 ASSERT(args.length() == 1);
6001
6002 // Check arguments.
6003 Object* result = Runtime_CheckExecutionState(args);
6004 if (result->IsFailure()) return result;
6005
6006 // Count all frames which are relevant to debugging stack trace.
6007 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006008 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006009 if (id == StackFrame::NO_ID) {
6010 // If there is no JavaScript stack frame count is 0.
6011 return Smi::FromInt(0);
6012 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006013 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6014 return Smi::FromInt(n);
6015}
6016
6017
6018static const int kFrameDetailsFrameIdIndex = 0;
6019static const int kFrameDetailsReceiverIndex = 1;
6020static const int kFrameDetailsFunctionIndex = 2;
6021static const int kFrameDetailsArgumentCountIndex = 3;
6022static const int kFrameDetailsLocalCountIndex = 4;
6023static const int kFrameDetailsSourcePositionIndex = 5;
6024static const int kFrameDetailsConstructCallIndex = 6;
6025static const int kFrameDetailsDebuggerFrameIndex = 7;
6026static const int kFrameDetailsFirstDynamicIndex = 8;
6027
6028// Return an array with frame details
6029// args[0]: number: break id
6030// args[1]: number: frame index
6031//
6032// The array returned contains the following information:
6033// 0: Frame id
6034// 1: Receiver
6035// 2: Function
6036// 3: Argument count
6037// 4: Local count
6038// 5: Source position
6039// 6: Constructor call
6040// 7: Debugger frame
6041// Arguments name, value
6042// Locals name, value
6043static Object* Runtime_GetFrameDetails(Arguments args) {
6044 HandleScope scope;
6045 ASSERT(args.length() == 2);
6046
6047 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006048 Object* check = Runtime_CheckExecutionState(args);
6049 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006050 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6051
6052 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006053 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006054 if (id == StackFrame::NO_ID) {
6055 // If there are no JavaScript stack frames return undefined.
6056 return Heap::undefined_value();
6057 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006058 int count = 0;
6059 JavaScriptFrameIterator it(id);
6060 for (; !it.done(); it.Advance()) {
6061 if (count == index) break;
6062 count++;
6063 }
6064 if (it.done()) return Heap::undefined_value();
6065
6066 // Traverse the saved contexts chain to find the active context for the
6067 // selected frame.
6068 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006069 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006070 save = save->prev();
6071 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006072 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006073
6074 // Get the frame id.
6075 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6076
6077 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006078 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006079
6080 // Check for constructor frame.
6081 bool constructor = it.frame()->IsConstructor();
6082
6083 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006084 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006085 ScopeInfo<> info(*code);
6086
6087 // Get the context.
6088 Handle<Context> context(Context::cast(it.frame()->context()));
6089
6090 // Get the locals names and values into a temporary array.
6091 //
6092 // TODO(1240907): Hide compiler-introduced stack variables
6093 // (e.g. .result)? For users of the debugger, they will probably be
6094 // confusing.
6095 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6096 for (int i = 0; i < info.NumberOfLocals(); i++) {
6097 // Name of the local.
6098 locals->set(i * 2, *info.LocalName(i));
6099
6100 // Fetch the value of the local - either from the stack or from a
6101 // heap-allocated context.
6102 if (i < info.number_of_stack_slots()) {
6103 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6104 } else {
6105 Handle<String> name = info.LocalName(i);
6106 // Traverse the context chain to the function context as all local
6107 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006108 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006109 context = Handle<Context>(context->previous());
6110 }
6111 ASSERT(context->is_function_context());
6112 locals->set(i * 2 + 1,
6113 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6114 NULL)));
6115 }
6116 }
6117
6118 // Now advance to the arguments adapter frame (if any). If contains all
6119 // the provided parameters and
6120
6121 // Now advance to the arguments adapter frame (if any). It contains all
6122 // the provided parameters whereas the function frame always have the number
6123 // of arguments matching the functions parameters. The rest of the
6124 // information (except for what is collected above) is the same.
6125 it.AdvanceToArgumentsFrame();
6126
6127 // Find the number of arguments to fill. At least fill the number of
6128 // parameters for the function and fill more if more parameters are provided.
6129 int argument_count = info.number_of_parameters();
6130 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6131 argument_count = it.frame()->GetProvidedParametersCount();
6132 }
6133
6134 // Calculate the size of the result.
6135 int details_size = kFrameDetailsFirstDynamicIndex +
6136 2 * (argument_count + info.NumberOfLocals());
6137 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6138
6139 // Add the frame id.
6140 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6141
6142 // Add the function (same as in function frame).
6143 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6144
6145 // Add the arguments count.
6146 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6147
6148 // Add the locals count
6149 details->set(kFrameDetailsLocalCountIndex,
6150 Smi::FromInt(info.NumberOfLocals()));
6151
6152 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006153 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006154 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6155 } else {
6156 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6157 }
6158
6159 // Add the constructor information.
6160 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6161
6162 // Add information on whether this frame is invoked in the debugger context.
6163 details->set(kFrameDetailsDebuggerFrameIndex,
6164 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6165
6166 // Fill the dynamic part.
6167 int details_index = kFrameDetailsFirstDynamicIndex;
6168
6169 // Add arguments name and value.
6170 for (int i = 0; i < argument_count; i++) {
6171 // Name of the argument.
6172 if (i < info.number_of_parameters()) {
6173 details->set(details_index++, *info.parameter_name(i));
6174 } else {
6175 details->set(details_index++, Heap::undefined_value());
6176 }
6177
6178 // Parameter value.
6179 if (i < it.frame()->GetProvidedParametersCount()) {
6180 details->set(details_index++, it.frame()->GetParameter(i));
6181 } else {
6182 details->set(details_index++, Heap::undefined_value());
6183 }
6184 }
6185
6186 // Add locals name and value from the temporary copy from the function frame.
6187 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6188 details->set(details_index++, locals->get(i));
6189 }
6190
6191 // Add the receiver (same as in function frame).
6192 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6193 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6194 Handle<Object> receiver(it.frame()->receiver());
6195 if (!receiver->IsJSObject()) {
6196 // If the receiver is NOT a JSObject we have hit an optimization
6197 // where a value object is not converted into a wrapped JS objects.
6198 // To hide this optimization from the debugger, we wrap the receiver
6199 // by creating correct wrapper object based on the calling frame's
6200 // global context.
6201 it.Advance();
6202 Handle<Context> calling_frames_global_context(
6203 Context::cast(Context::cast(it.frame()->context())->global_context()));
6204 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6205 }
6206 details->set(kFrameDetailsReceiverIndex, *receiver);
6207
6208 ASSERT_EQ(details_size, details_index);
6209 return *Factory::NewJSArrayWithElements(details);
6210}
6211
6212
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006213// Copy all the context locals into an object used to materialize a scope.
6214static void CopyContextLocalsToScopeObject(Handle<Code> code,
6215 ScopeInfo<>& scope_info,
6216 Handle<Context> context,
6217 Handle<JSObject> scope_object) {
6218 // Fill all context locals to the context extension.
6219 for (int i = Context::MIN_CONTEXT_SLOTS;
6220 i < scope_info.number_of_context_slots();
6221 i++) {
6222 int context_index =
6223 ScopeInfo<>::ContextSlotIndex(*code,
6224 *scope_info.context_slot_name(i),
6225 NULL);
6226
6227 // Don't include the arguments shadow (.arguments) context variable.
6228 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6229 SetProperty(scope_object,
6230 scope_info.context_slot_name(i),
6231 Handle<Object>(context->get(context_index)), NONE);
6232 }
6233 }
6234}
6235
6236
6237// Create a plain JSObject which materializes the local scope for the specified
6238// frame.
6239static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6240 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6241 Handle<Code> code(function->code());
6242 ScopeInfo<> scope_info(*code);
6243
6244 // Allocate and initialize a JSObject with all the arguments, stack locals
6245 // heap locals and extension properties of the debugged function.
6246 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6247
6248 // First fill all parameters.
6249 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6250 SetProperty(local_scope,
6251 scope_info.parameter_name(i),
6252 Handle<Object>(frame->GetParameter(i)), NONE);
6253 }
6254
6255 // Second fill all stack locals.
6256 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6257 SetProperty(local_scope,
6258 scope_info.stack_slot_name(i),
6259 Handle<Object>(frame->GetExpression(i)), NONE);
6260 }
6261
6262 // Third fill all context locals.
6263 Handle<Context> frame_context(Context::cast(frame->context()));
6264 Handle<Context> function_context(frame_context->fcontext());
6265 CopyContextLocalsToScopeObject(code, scope_info,
6266 function_context, local_scope);
6267
6268 // Finally copy any properties from the function context extension. This will
6269 // be variables introduced by eval.
6270 if (function_context->closure() == *function) {
6271 if (function_context->has_extension() &&
6272 !function_context->IsGlobalContext()) {
6273 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6274 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6275 for (int i = 0; i < keys->length(); i++) {
6276 // Names of variables introduced by eval are strings.
6277 ASSERT(keys->get(i)->IsString());
6278 Handle<String> key(String::cast(keys->get(i)));
6279 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6280 }
6281 }
6282 }
6283 return local_scope;
6284}
6285
6286
6287// Create a plain JSObject which materializes the closure content for the
6288// context.
6289static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6290 ASSERT(context->is_function_context());
6291
6292 Handle<Code> code(context->closure()->code());
6293 ScopeInfo<> scope_info(*code);
6294
6295 // Allocate and initialize a JSObject with all the content of theis function
6296 // closure.
6297 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6298
6299 // Check whether the arguments shadow object exists.
6300 int arguments_shadow_index =
6301 ScopeInfo<>::ContextSlotIndex(*code,
6302 Heap::arguments_shadow_symbol(),
6303 NULL);
6304 if (arguments_shadow_index >= 0) {
6305 // In this case all the arguments are available in the arguments shadow
6306 // object.
6307 Handle<JSObject> arguments_shadow(
6308 JSObject::cast(context->get(arguments_shadow_index)));
6309 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6310 SetProperty(closure_scope,
6311 scope_info.parameter_name(i),
6312 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6313 }
6314 }
6315
6316 // Fill all context locals to the context extension.
6317 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6318
6319 // Finally copy any properties from the function context extension. This will
6320 // be variables introduced by eval.
6321 if (context->has_extension()) {
6322 Handle<JSObject> ext(JSObject::cast(context->extension()));
6323 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6324 for (int i = 0; i < keys->length(); i++) {
6325 // Names of variables introduced by eval are strings.
6326 ASSERT(keys->get(i)->IsString());
6327 Handle<String> key(String::cast(keys->get(i)));
6328 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6329 }
6330 }
6331
6332 return closure_scope;
6333}
6334
6335
6336// Iterate over the actual scopes visible from a stack frame. All scopes are
6337// backed by an actual context except the local scope, which is inserted
6338// "artifically" in the context chain.
6339class ScopeIterator {
6340 public:
6341 enum ScopeType {
6342 ScopeTypeGlobal = 0,
6343 ScopeTypeLocal,
6344 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006345 ScopeTypeClosure,
6346 // Every catch block contains an implicit with block (its parameter is
6347 // a JSContextExtensionObject) that extends current scope with a variable
6348 // holding exception object. Such with blocks are treated as scopes of their
6349 // own type.
6350 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006351 };
6352
6353 explicit ScopeIterator(JavaScriptFrame* frame)
6354 : frame_(frame),
6355 function_(JSFunction::cast(frame->function())),
6356 context_(Context::cast(frame->context())),
6357 local_done_(false),
6358 at_local_(false) {
6359
6360 // Check whether the first scope is actually a local scope.
6361 if (context_->IsGlobalContext()) {
6362 // If there is a stack slot for .result then this local scope has been
6363 // created for evaluating top level code and it is not a real local scope.
6364 // Checking for the existence of .result seems fragile, but the scope info
6365 // saved with the code object does not otherwise have that information.
6366 Handle<Code> code(function_->code());
6367 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6368 at_local_ = index < 0;
6369 } else if (context_->is_function_context()) {
6370 at_local_ = true;
6371 }
6372 }
6373
6374 // More scopes?
6375 bool Done() { return context_.is_null(); }
6376
6377 // Move to the next scope.
6378 void Next() {
6379 // If at a local scope mark the local scope as passed.
6380 if (at_local_) {
6381 at_local_ = false;
6382 local_done_ = true;
6383
6384 // If the current context is not associated with the local scope the
6385 // current context is the next real scope, so don't move to the next
6386 // context in this case.
6387 if (context_->closure() != *function_) {
6388 return;
6389 }
6390 }
6391
6392 // The global scope is always the last in the chain.
6393 if (context_->IsGlobalContext()) {
6394 context_ = Handle<Context>();
6395 return;
6396 }
6397
6398 // Move to the next context.
6399 if (context_->is_function_context()) {
6400 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6401 } else {
6402 context_ = Handle<Context>(context_->previous());
6403 }
6404
6405 // If passing the local scope indicate that the current scope is now the
6406 // local scope.
6407 if (!local_done_ &&
6408 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6409 at_local_ = true;
6410 }
6411 }
6412
6413 // Return the type of the current scope.
6414 int Type() {
6415 if (at_local_) {
6416 return ScopeTypeLocal;
6417 }
6418 if (context_->IsGlobalContext()) {
6419 ASSERT(context_->global()->IsGlobalObject());
6420 return ScopeTypeGlobal;
6421 }
6422 if (context_->is_function_context()) {
6423 return ScopeTypeClosure;
6424 }
6425 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006426 // Current scope is either an explicit with statement or a with statement
6427 // implicitely generated for a catch block.
6428 // If the extension object here is a JSContextExtensionObject then
6429 // current with statement is one frome a catch block otherwise it's a
6430 // regular with statement.
6431 if (context_->extension()->IsJSContextExtensionObject()) {
6432 return ScopeTypeCatch;
6433 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006434 return ScopeTypeWith;
6435 }
6436
6437 // Return the JavaScript object with the content of the current scope.
6438 Handle<JSObject> ScopeObject() {
6439 switch (Type()) {
6440 case ScopeIterator::ScopeTypeGlobal:
6441 return Handle<JSObject>(CurrentContext()->global());
6442 break;
6443 case ScopeIterator::ScopeTypeLocal:
6444 // Materialize the content of the local scope into a JSObject.
6445 return MaterializeLocalScope(frame_);
6446 break;
6447 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006448 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006449 // Return the with object.
6450 return Handle<JSObject>(CurrentContext()->extension());
6451 break;
6452 case ScopeIterator::ScopeTypeClosure:
6453 // Materialize the content of the closure scope into a JSObject.
6454 return MaterializeClosure(CurrentContext());
6455 break;
6456 }
6457 UNREACHABLE();
6458 return Handle<JSObject>();
6459 }
6460
6461 // Return the context for this scope. For the local context there might not
6462 // be an actual context.
6463 Handle<Context> CurrentContext() {
6464 if (at_local_ && context_->closure() != *function_) {
6465 return Handle<Context>();
6466 }
6467 return context_;
6468 }
6469
6470#ifdef DEBUG
6471 // Debug print of the content of the current scope.
6472 void DebugPrint() {
6473 switch (Type()) {
6474 case ScopeIterator::ScopeTypeGlobal:
6475 PrintF("Global:\n");
6476 CurrentContext()->Print();
6477 break;
6478
6479 case ScopeIterator::ScopeTypeLocal: {
6480 PrintF("Local:\n");
6481 Handle<Code> code(function_->code());
6482 ScopeInfo<> scope_info(*code);
6483 scope_info.Print();
6484 if (!CurrentContext().is_null()) {
6485 CurrentContext()->Print();
6486 if (CurrentContext()->has_extension()) {
6487 Handle<JSObject> extension =
6488 Handle<JSObject>(CurrentContext()->extension());
6489 if (extension->IsJSContextExtensionObject()) {
6490 extension->Print();
6491 }
6492 }
6493 }
6494 break;
6495 }
6496
6497 case ScopeIterator::ScopeTypeWith: {
6498 PrintF("With:\n");
6499 Handle<JSObject> extension =
6500 Handle<JSObject>(CurrentContext()->extension());
6501 extension->Print();
6502 break;
6503 }
6504
ager@chromium.orga1645e22009-09-09 19:27:10 +00006505 case ScopeIterator::ScopeTypeCatch: {
6506 PrintF("Catch:\n");
6507 Handle<JSObject> extension =
6508 Handle<JSObject>(CurrentContext()->extension());
6509 extension->Print();
6510 break;
6511 }
6512
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006513 case ScopeIterator::ScopeTypeClosure: {
6514 PrintF("Closure:\n");
6515 CurrentContext()->Print();
6516 if (CurrentContext()->has_extension()) {
6517 Handle<JSObject> extension =
6518 Handle<JSObject>(CurrentContext()->extension());
6519 if (extension->IsJSContextExtensionObject()) {
6520 extension->Print();
6521 }
6522 }
6523 break;
6524 }
6525
6526 default:
6527 UNREACHABLE();
6528 }
6529 PrintF("\n");
6530 }
6531#endif
6532
6533 private:
6534 JavaScriptFrame* frame_;
6535 Handle<JSFunction> function_;
6536 Handle<Context> context_;
6537 bool local_done_;
6538 bool at_local_;
6539
6540 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6541};
6542
6543
6544static Object* Runtime_GetScopeCount(Arguments args) {
6545 HandleScope scope;
6546 ASSERT(args.length() == 2);
6547
6548 // Check arguments.
6549 Object* check = Runtime_CheckExecutionState(args);
6550 if (check->IsFailure()) return check;
6551 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6552
6553 // Get the frame where the debugging is performed.
6554 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6555 JavaScriptFrameIterator it(id);
6556 JavaScriptFrame* frame = it.frame();
6557
6558 // Count the visible scopes.
6559 int n = 0;
6560 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6561 n++;
6562 }
6563
6564 return Smi::FromInt(n);
6565}
6566
6567
6568static const int kScopeDetailsTypeIndex = 0;
6569static const int kScopeDetailsObjectIndex = 1;
6570static const int kScopeDetailsSize = 2;
6571
6572// Return an array with scope details
6573// args[0]: number: break id
6574// args[1]: number: frame index
6575// args[2]: number: scope index
6576//
6577// The array returned contains the following information:
6578// 0: Scope type
6579// 1: Scope object
6580static Object* Runtime_GetScopeDetails(Arguments args) {
6581 HandleScope scope;
6582 ASSERT(args.length() == 3);
6583
6584 // Check arguments.
6585 Object* check = Runtime_CheckExecutionState(args);
6586 if (check->IsFailure()) return check;
6587 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6588 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6589
6590 // Get the frame where the debugging is performed.
6591 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6592 JavaScriptFrameIterator frame_it(id);
6593 JavaScriptFrame* frame = frame_it.frame();
6594
6595 // Find the requested scope.
6596 int n = 0;
6597 ScopeIterator it(frame);
6598 for (; !it.Done() && n < index; it.Next()) {
6599 n++;
6600 }
6601 if (it.Done()) {
6602 return Heap::undefined_value();
6603 }
6604
6605 // Calculate the size of the result.
6606 int details_size = kScopeDetailsSize;
6607 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6608
6609 // Fill in scope details.
6610 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6611 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6612
6613 return *Factory::NewJSArrayWithElements(details);
6614}
6615
6616
6617static Object* Runtime_DebugPrintScopes(Arguments args) {
6618 HandleScope scope;
6619 ASSERT(args.length() == 0);
6620
6621#ifdef DEBUG
6622 // Print the scopes for the top frame.
6623 StackFrameLocator locator;
6624 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6625 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6626 it.DebugPrint();
6627 }
6628#endif
6629 return Heap::undefined_value();
6630}
6631
6632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006633static Object* Runtime_GetCFrames(Arguments args) {
6634 HandleScope scope;
6635 ASSERT(args.length() == 1);
6636 Object* result = Runtime_CheckExecutionState(args);
6637 if (result->IsFailure()) return result;
6638
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006639#if V8_HOST_ARCH_64_BIT
6640 UNIMPLEMENTED();
6641 return Heap::undefined_value();
6642#else
6643
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006644 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006645 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6646 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006647 if (frames_count == OS::kStackWalkError) {
6648 return Heap::undefined_value();
6649 }
6650
6651 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6652 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6653 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6654 for (int i = 0; i < frames_count; i++) {
6655 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6656 frame_value->SetProperty(
6657 *address_str,
6658 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6659 NONE);
6660
6661 // Get the stack walk text for this frame.
6662 Handle<String> frame_text;
6663 if (strlen(frames[i].text) > 0) {
6664 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6665 frame_text = Factory::NewStringFromAscii(str);
6666 }
6667
6668 if (!frame_text.is_null()) {
6669 frame_value->SetProperty(*text_str, *frame_text, NONE);
6670 }
6671
6672 frames_array->set(i, *frame_value);
6673 }
6674 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006675#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006676}
6677
6678
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006679static Object* Runtime_GetThreadCount(Arguments args) {
6680 HandleScope scope;
6681 ASSERT(args.length() == 1);
6682
6683 // Check arguments.
6684 Object* result = Runtime_CheckExecutionState(args);
6685 if (result->IsFailure()) return result;
6686
6687 // Count all archived V8 threads.
6688 int n = 0;
6689 for (ThreadState* thread = ThreadState::FirstInUse();
6690 thread != NULL;
6691 thread = thread->Next()) {
6692 n++;
6693 }
6694
6695 // Total number of threads is current thread and archived threads.
6696 return Smi::FromInt(n + 1);
6697}
6698
6699
6700static const int kThreadDetailsCurrentThreadIndex = 0;
6701static const int kThreadDetailsThreadIdIndex = 1;
6702static const int kThreadDetailsSize = 2;
6703
6704// Return an array with thread details
6705// args[0]: number: break id
6706// args[1]: number: thread index
6707//
6708// The array returned contains the following information:
6709// 0: Is current thread?
6710// 1: Thread id
6711static Object* Runtime_GetThreadDetails(Arguments args) {
6712 HandleScope scope;
6713 ASSERT(args.length() == 2);
6714
6715 // Check arguments.
6716 Object* check = Runtime_CheckExecutionState(args);
6717 if (check->IsFailure()) return check;
6718 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6719
6720 // Allocate array for result.
6721 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6722
6723 // Thread index 0 is current thread.
6724 if (index == 0) {
6725 // Fill the details.
6726 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6727 details->set(kThreadDetailsThreadIdIndex,
6728 Smi::FromInt(ThreadManager::CurrentId()));
6729 } else {
6730 // Find the thread with the requested index.
6731 int n = 1;
6732 ThreadState* thread = ThreadState::FirstInUse();
6733 while (index != n && thread != NULL) {
6734 thread = thread->Next();
6735 n++;
6736 }
6737 if (thread == NULL) {
6738 return Heap::undefined_value();
6739 }
6740
6741 // Fill the details.
6742 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6743 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6744 }
6745
6746 // Convert to JS array and return.
6747 return *Factory::NewJSArrayWithElements(details);
6748}
6749
6750
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006751static Object* Runtime_GetBreakLocations(Arguments args) {
6752 HandleScope scope;
6753 ASSERT(args.length() == 1);
6754
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006755 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6756 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006757 // Find the number of break points
6758 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6759 if (break_locations->IsUndefined()) return Heap::undefined_value();
6760 // Return array as JS array
6761 return *Factory::NewJSArrayWithElements(
6762 Handle<FixedArray>::cast(break_locations));
6763}
6764
6765
6766// Set a break point in a function
6767// args[0]: function
6768// args[1]: number: break source position (within the function source)
6769// args[2]: number: break point object
6770static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6771 HandleScope scope;
6772 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006773 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6774 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006775 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6776 RUNTIME_ASSERT(source_position >= 0);
6777 Handle<Object> break_point_object_arg = args.at<Object>(2);
6778
6779 // Set break point.
6780 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6781
6782 return Heap::undefined_value();
6783}
6784
6785
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006786Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6787 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006788 // Iterate the heap looking for SharedFunctionInfo generated from the
6789 // script. The inner most SharedFunctionInfo containing the source position
6790 // for the requested break point is found.
6791 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6792 // which is found is not compiled it is compiled and the heap is iterated
6793 // again as the compilation might create inner functions from the newly
6794 // compiled function and the actual requested break point might be in one of
6795 // these functions.
6796 bool done = false;
6797 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006798 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006799 Handle<SharedFunctionInfo> target;
6800 // The current candidate for the last function in script:
6801 Handle<SharedFunctionInfo> last;
6802 while (!done) {
6803 HeapIterator iterator;
6804 while (iterator.has_next()) {
6805 HeapObject* obj = iterator.next();
6806 ASSERT(obj != NULL);
6807 if (obj->IsSharedFunctionInfo()) {
6808 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6809 if (shared->script() == *script) {
6810 // If the SharedFunctionInfo found has the requested script data and
6811 // contains the source position it is a candidate.
6812 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006813 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006814 start_position = shared->start_position();
6815 }
6816 if (start_position <= position &&
6817 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006818 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006819 // candidate this is the new candidate.
6820 if (target.is_null()) {
6821 target_start_position = start_position;
6822 target = shared;
6823 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00006824 if (target_start_position == start_position &&
6825 shared->end_position() == target->end_position()) {
6826 // If a top-level function contain only one function
6827 // declartion the source for the top-level and the function is
6828 // the same. In that case prefer the non top-level function.
6829 if (!shared->is_toplevel()) {
6830 target_start_position = start_position;
6831 target = shared;
6832 }
6833 } else if (target_start_position <= start_position &&
6834 shared->end_position() <= target->end_position()) {
6835 // This containment check includes equality as a function inside
6836 // a top-level function can share either start or end position
6837 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006838 target_start_position = start_position;
6839 target = shared;
6840 }
6841 }
6842 }
6843
6844 // Keep track of the last function in the script.
6845 if (last.is_null() ||
6846 shared->end_position() > last->start_position()) {
6847 last = shared;
6848 }
6849 }
6850 }
6851 }
6852
6853 // Make sure some candidate is selected.
6854 if (target.is_null()) {
6855 if (!last.is_null()) {
6856 // Position after the last function - use last.
6857 target = last;
6858 } else {
6859 // Unable to find function - possibly script without any function.
6860 return Heap::undefined_value();
6861 }
6862 }
6863
6864 // If the candidate found is compiled we are done. NOTE: when lazy
6865 // compilation of inner functions is introduced some additional checking
6866 // needs to be done here to compile inner functions.
6867 done = target->is_compiled();
6868 if (!done) {
6869 // If the candidate is not compiled compile it to reveal any inner
6870 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006871 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006872 }
6873 }
6874
6875 return *target;
6876}
6877
6878
6879// Change the state of a break point in a script. NOTE: Regarding performance
6880// see the NOTE for GetScriptFromScriptData.
6881// args[0]: script to set break point in
6882// args[1]: number: break source position (within the script source)
6883// args[2]: number: break point object
6884static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6885 HandleScope scope;
6886 ASSERT(args.length() == 3);
6887 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6888 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6889 RUNTIME_ASSERT(source_position >= 0);
6890 Handle<Object> break_point_object_arg = args.at<Object>(2);
6891
6892 // Get the script from the script wrapper.
6893 RUNTIME_ASSERT(wrapper->value()->IsScript());
6894 Handle<Script> script(Script::cast(wrapper->value()));
6895
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006896 Object* result = Runtime::FindSharedFunctionInfoInScript(
6897 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006898 if (!result->IsUndefined()) {
6899 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6900 // Find position within function. The script position might be before the
6901 // source position of the first function.
6902 int position;
6903 if (shared->start_position() > source_position) {
6904 position = 0;
6905 } else {
6906 position = source_position - shared->start_position();
6907 }
6908 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6909 }
6910 return Heap::undefined_value();
6911}
6912
6913
6914// Clear a break point
6915// args[0]: number: break point object
6916static Object* Runtime_ClearBreakPoint(Arguments args) {
6917 HandleScope scope;
6918 ASSERT(args.length() == 1);
6919 Handle<Object> break_point_object_arg = args.at<Object>(0);
6920
6921 // Clear break point.
6922 Debug::ClearBreakPoint(break_point_object_arg);
6923
6924 return Heap::undefined_value();
6925}
6926
6927
6928// Change the state of break on exceptions
6929// args[0]: boolean indicating uncaught exceptions
6930// args[1]: boolean indicating on/off
6931static Object* Runtime_ChangeBreakOnException(Arguments args) {
6932 HandleScope scope;
6933 ASSERT(args.length() == 2);
6934 ASSERT(args[0]->IsNumber());
6935 ASSERT(args[1]->IsBoolean());
6936
6937 // Update break point state
6938 ExceptionBreakType type =
6939 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6940 bool enable = args[1]->ToBoolean()->IsTrue();
6941 Debug::ChangeBreakOnException(type, enable);
6942 return Heap::undefined_value();
6943}
6944
6945
6946// Prepare for stepping
6947// args[0]: break id for checking execution state
6948// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00006949// args[2]: number of times to perform the step, for step out it is the number
6950// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006951static Object* Runtime_PrepareStep(Arguments args) {
6952 HandleScope scope;
6953 ASSERT(args.length() == 3);
6954 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006955 Object* check = Runtime_CheckExecutionState(args);
6956 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006957 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6958 return Top::Throw(Heap::illegal_argument_symbol());
6959 }
6960
6961 // Get the step action and check validity.
6962 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6963 if (step_action != StepIn &&
6964 step_action != StepNext &&
6965 step_action != StepOut &&
6966 step_action != StepInMin &&
6967 step_action != StepMin) {
6968 return Top::Throw(Heap::illegal_argument_symbol());
6969 }
6970
6971 // Get the number of steps.
6972 int step_count = NumberToInt32(args[2]);
6973 if (step_count < 1) {
6974 return Top::Throw(Heap::illegal_argument_symbol());
6975 }
6976
ager@chromium.orga1645e22009-09-09 19:27:10 +00006977 // Clear all current stepping setup.
6978 Debug::ClearStepping();
6979
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006980 // Prepare step.
6981 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6982 return Heap::undefined_value();
6983}
6984
6985
6986// Clear all stepping set by PrepareStep.
6987static Object* Runtime_ClearStepping(Arguments args) {
6988 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006989 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006990 Debug::ClearStepping();
6991 return Heap::undefined_value();
6992}
6993
6994
6995// Creates a copy of the with context chain. The copy of the context chain is
6996// is linked to the function context supplied.
6997static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6998 Handle<Context> function_context) {
6999 // At the bottom of the chain. Return the function context to link to.
7000 if (context_chain->is_function_context()) {
7001 return function_context;
7002 }
7003
7004 // Recursively copy the with contexts.
7005 Handle<Context> previous(context_chain->previous());
7006 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7007 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007008 CopyWithContextChain(function_context, previous),
7009 extension,
7010 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007011}
7012
7013
7014// Helper function to find or create the arguments object for
7015// Runtime_DebugEvaluate.
7016static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7017 Handle<JSFunction> function,
7018 Handle<Code> code,
7019 const ScopeInfo<>* sinfo,
7020 Handle<Context> function_context) {
7021 // Try to find the value of 'arguments' to pass as parameter. If it is not
7022 // found (that is the debugged function does not reference 'arguments' and
7023 // does not support eval) then create an 'arguments' object.
7024 int index;
7025 if (sinfo->number_of_stack_slots() > 0) {
7026 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7027 if (index != -1) {
7028 return Handle<Object>(frame->GetExpression(index));
7029 }
7030 }
7031
7032 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7033 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7034 NULL);
7035 if (index != -1) {
7036 return Handle<Object>(function_context->get(index));
7037 }
7038 }
7039
7040 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007041 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7042 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007043 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007044 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007045 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007046 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007047 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007048 return arguments;
7049}
7050
7051
7052// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007053// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054// extension part has all the parameters and locals of the function on the
7055// stack frame. A function which calls eval with the code to evaluate is then
7056// compiled in this context and called in this context. As this context
7057// replaces the context of the function on the stack frame a new (empty)
7058// function is created as well to be used as the closure for the context.
7059// This function and the context acts as replacements for the function on the
7060// stack frame presenting the same view of the values of parameters and
7061// local variables as if the piece of JavaScript was evaluated at the point
7062// where the function on the stack frame is currently stopped.
7063static Object* Runtime_DebugEvaluate(Arguments args) {
7064 HandleScope scope;
7065
7066 // Check the execution state and decode arguments frame and source to be
7067 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007068 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007069 Object* check_result = Runtime_CheckExecutionState(args);
7070 if (check_result->IsFailure()) return check_result;
7071 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7072 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007073 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7074
7075 // Handle the processing of break.
7076 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007077
7078 // Get the frame where the debugging is performed.
7079 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7080 JavaScriptFrameIterator it(id);
7081 JavaScriptFrame* frame = it.frame();
7082 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7083 Handle<Code> code(function->code());
7084 ScopeInfo<> sinfo(*code);
7085
7086 // Traverse the saved contexts chain to find the active context for the
7087 // selected frame.
7088 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007089 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007090 save = save->prev();
7091 }
7092 ASSERT(save != NULL);
7093 SaveContext savex;
7094 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007095
7096 // Create the (empty) function replacing the function on the stack frame for
7097 // the purpose of evaluating in the context created below. It is important
7098 // that this function does not describe any parameters and local variables
7099 // in the context. If it does then this will cause problems with the lookup
7100 // in Context::Lookup, where context slots for parameters and local variables
7101 // are looked at before the extension object.
7102 Handle<JSFunction> go_between =
7103 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7104 go_between->set_context(function->context());
7105#ifdef DEBUG
7106 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7107 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7108 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7109#endif
7110
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007111 // Materialize the content of the local scope into a JSObject.
7112 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007113
7114 // Allocate a new context for the debug evaluation and set the extension
7115 // object build.
7116 Handle<Context> context =
7117 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007118 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007119 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007120 Handle<Context> frame_context(Context::cast(frame->context()));
7121 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007122 context = CopyWithContextChain(frame_context, context);
7123
7124 // Wrap the evaluation statement in a new function compiled in the newly
7125 // created context. The function has one parameter which has to be called
7126 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007127 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007128 // function(arguments,__source__) {return eval(__source__);}
7129 static const char* source_str =
7130 "function(arguments,__source__){return eval(__source__);}";
7131 static const int source_str_length = strlen(source_str);
7132 Handle<String> function_source =
7133 Factory::NewStringFromAscii(Vector<const char>(source_str,
7134 source_str_length));
7135 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007136 Compiler::CompileEval(function_source,
7137 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007138 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007139 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007140 if (boilerplate.is_null()) return Failure::Exception();
7141 Handle<JSFunction> compiled_function =
7142 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7143
7144 // Invoke the result of the compilation to get the evaluation function.
7145 bool has_pending_exception;
7146 Handle<Object> receiver(frame->receiver());
7147 Handle<Object> evaluation_function =
7148 Execution::Call(compiled_function, receiver, 0, NULL,
7149 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007150 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007151
7152 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7153 function_context);
7154
7155 // Invoke the evaluation function and return the result.
7156 const int argc = 2;
7157 Object** argv[argc] = { arguments.location(),
7158 Handle<Object>::cast(source).location() };
7159 Handle<Object> result =
7160 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7161 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007162 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007163
7164 // Skip the global proxy as it has no properties and always delegates to the
7165 // real global object.
7166 if (result->IsJSGlobalProxy()) {
7167 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7168 }
7169
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007170 return *result;
7171}
7172
7173
7174static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7175 HandleScope scope;
7176
7177 // Check the execution state and decode arguments frame and source to be
7178 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007179 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007180 Object* check_result = Runtime_CheckExecutionState(args);
7181 if (check_result->IsFailure()) return check_result;
7182 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007183 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7184
7185 // Handle the processing of break.
7186 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007187
7188 // Enter the top context from before the debugger was invoked.
7189 SaveContext save;
7190 SaveContext* top = &save;
7191 while (top != NULL && *top->context() == *Debug::debug_context()) {
7192 top = top->prev();
7193 }
7194 if (top != NULL) {
7195 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007196 }
7197
7198 // Get the global context now set to the top context from before the
7199 // debugger was invoked.
7200 Handle<Context> context = Top::global_context();
7201
7202 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007203 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007204 Handle<JSFunction>(Compiler::CompileEval(source,
7205 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007206 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007207 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007208 if (boilerplate.is_null()) return Failure::Exception();
7209 Handle<JSFunction> compiled_function =
7210 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7211 context));
7212
7213 // Invoke the result of the compilation to get the evaluation function.
7214 bool has_pending_exception;
7215 Handle<Object> receiver = Top::global();
7216 Handle<Object> result =
7217 Execution::Call(compiled_function, receiver, 0, NULL,
7218 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007219 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007220 return *result;
7221}
7222
7223
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007224static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7225 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007226 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007227
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007228 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007229 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007230
7231 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007232 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007233 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7234 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7235 // because using
7236 // instances->set(i, *GetScriptWrapper(script))
7237 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7238 // already have deferenced the instances handle.
7239 Handle<JSValue> wrapper = GetScriptWrapper(script);
7240 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007241 }
7242
7243 // Return result as a JS array.
7244 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7245 Handle<JSArray>::cast(result)->SetContent(*instances);
7246 return *result;
7247}
7248
7249
7250// Helper function used by Runtime_DebugReferencedBy below.
7251static int DebugReferencedBy(JSObject* target,
7252 Object* instance_filter, int max_references,
7253 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007254 JSFunction* arguments_function) {
7255 NoHandleAllocation ha;
7256 AssertNoAllocation no_alloc;
7257
7258 // Iterate the heap.
7259 int count = 0;
7260 JSObject* last = NULL;
7261 HeapIterator iterator;
7262 while (iterator.has_next() &&
7263 (max_references == 0 || count < max_references)) {
7264 // Only look at all JSObjects.
7265 HeapObject* heap_obj = iterator.next();
7266 if (heap_obj->IsJSObject()) {
7267 // Skip context extension objects and argument arrays as these are
7268 // checked in the context of functions using them.
7269 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007270 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007271 obj->map()->constructor() == arguments_function) {
7272 continue;
7273 }
7274
7275 // Check if the JS object has a reference to the object looked for.
7276 if (obj->ReferencesObject(target)) {
7277 // Check instance filter if supplied. This is normally used to avoid
7278 // references from mirror objects (see Runtime_IsInPrototypeChain).
7279 if (!instance_filter->IsUndefined()) {
7280 Object* V = obj;
7281 while (true) {
7282 Object* prototype = V->GetPrototype();
7283 if (prototype->IsNull()) {
7284 break;
7285 }
7286 if (instance_filter == prototype) {
7287 obj = NULL; // Don't add this object.
7288 break;
7289 }
7290 V = prototype;
7291 }
7292 }
7293
7294 if (obj != NULL) {
7295 // Valid reference found add to instance array if supplied an update
7296 // count.
7297 if (instances != NULL && count < instances_size) {
7298 instances->set(count, obj);
7299 }
7300 last = obj;
7301 count++;
7302 }
7303 }
7304 }
7305 }
7306
7307 // Check for circular reference only. This can happen when the object is only
7308 // referenced from mirrors and has a circular reference in which case the
7309 // object is not really alive and would have been garbage collected if not
7310 // referenced from the mirror.
7311 if (count == 1 && last == target) {
7312 count = 0;
7313 }
7314
7315 // Return the number of referencing objects found.
7316 return count;
7317}
7318
7319
7320// Scan the heap for objects with direct references to an object
7321// args[0]: the object to find references to
7322// args[1]: constructor function for instances to exclude (Mirror)
7323// args[2]: the the maximum number of objects to return
7324static Object* Runtime_DebugReferencedBy(Arguments args) {
7325 ASSERT(args.length() == 3);
7326
7327 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007328 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007329
7330 // Check parameters.
7331 CONVERT_CHECKED(JSObject, target, args[0]);
7332 Object* instance_filter = args[1];
7333 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7334 instance_filter->IsJSObject());
7335 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7336 RUNTIME_ASSERT(max_references >= 0);
7337
7338 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007339 JSObject* arguments_boilerplate =
7340 Top::context()->global_context()->arguments_boilerplate();
7341 JSFunction* arguments_function =
7342 JSFunction::cast(arguments_boilerplate->map()->constructor());
7343
7344 // Get the number of referencing objects.
7345 int count;
7346 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007347 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007348
7349 // Allocate an array to hold the result.
7350 Object* object = Heap::AllocateFixedArray(count);
7351 if (object->IsFailure()) return object;
7352 FixedArray* instances = FixedArray::cast(object);
7353
7354 // Fill the referencing objects.
7355 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007356 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007357
7358 // Return result as JS array.
7359 Object* result =
7360 Heap::AllocateJSObject(
7361 Top::context()->global_context()->array_function());
7362 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7363 return result;
7364}
7365
7366
7367// Helper function used by Runtime_DebugConstructedBy below.
7368static int DebugConstructedBy(JSFunction* constructor, int max_references,
7369 FixedArray* instances, int instances_size) {
7370 AssertNoAllocation no_alloc;
7371
7372 // Iterate the heap.
7373 int count = 0;
7374 HeapIterator iterator;
7375 while (iterator.has_next() &&
7376 (max_references == 0 || count < max_references)) {
7377 // Only look at all JSObjects.
7378 HeapObject* heap_obj = iterator.next();
7379 if (heap_obj->IsJSObject()) {
7380 JSObject* obj = JSObject::cast(heap_obj);
7381 if (obj->map()->constructor() == constructor) {
7382 // Valid reference found add to instance array if supplied an update
7383 // count.
7384 if (instances != NULL && count < instances_size) {
7385 instances->set(count, obj);
7386 }
7387 count++;
7388 }
7389 }
7390 }
7391
7392 // Return the number of referencing objects found.
7393 return count;
7394}
7395
7396
7397// Scan the heap for objects constructed by a specific function.
7398// args[0]: the constructor to find instances of
7399// args[1]: the the maximum number of objects to return
7400static Object* Runtime_DebugConstructedBy(Arguments args) {
7401 ASSERT(args.length() == 2);
7402
7403 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007404 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007405
7406 // Check parameters.
7407 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7408 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7409 RUNTIME_ASSERT(max_references >= 0);
7410
7411 // Get the number of referencing objects.
7412 int count;
7413 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7414
7415 // Allocate an array to hold the result.
7416 Object* object = Heap::AllocateFixedArray(count);
7417 if (object->IsFailure()) return object;
7418 FixedArray* instances = FixedArray::cast(object);
7419
7420 // Fill the referencing objects.
7421 count = DebugConstructedBy(constructor, max_references, instances, count);
7422
7423 // Return result as JS array.
7424 Object* result =
7425 Heap::AllocateJSObject(
7426 Top::context()->global_context()->array_function());
7427 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7428 return result;
7429}
7430
7431
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007432// Find the effective prototype object as returned by __proto__.
7433// args[0]: the object to find the prototype for.
7434static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007435 ASSERT(args.length() == 1);
7436
7437 CONVERT_CHECKED(JSObject, obj, args[0]);
7438
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007439 // Use the __proto__ accessor.
7440 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007441}
7442
7443
7444static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007445 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007446 CPU::DebugBreak();
7447 return Heap::undefined_value();
7448}
7449
7450
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007451static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007452#ifdef DEBUG
7453 HandleScope scope;
7454 ASSERT(args.length() == 1);
7455 // Get the function and make sure it is compiled.
7456 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7457 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7458 return Failure::Exception();
7459 }
7460 func->code()->PrintLn();
7461#endif // DEBUG
7462 return Heap::undefined_value();
7463}
ager@chromium.org9085a012009-05-11 19:22:57 +00007464
7465
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007466static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7467#ifdef DEBUG
7468 HandleScope scope;
7469 ASSERT(args.length() == 1);
7470 // Get the function and make sure it is compiled.
7471 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7472 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7473 return Failure::Exception();
7474 }
7475 func->shared()->construct_stub()->PrintLn();
7476#endif // DEBUG
7477 return Heap::undefined_value();
7478}
7479
7480
ager@chromium.org9085a012009-05-11 19:22:57 +00007481static Object* Runtime_FunctionGetInferredName(Arguments args) {
7482 NoHandleAllocation ha;
7483 ASSERT(args.length() == 1);
7484
7485 CONVERT_CHECKED(JSFunction, f, args[0]);
7486 return f->shared()->inferred_name();
7487}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007488#endif // ENABLE_DEBUGGER_SUPPORT
7489
7490
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007491// Finds the script object from the script data. NOTE: This operation uses
7492// heap traversal to find the function generated for the source position
7493// for the requested break point. For lazily compiled functions several heap
7494// traversals might be required rendering this operation as a rather slow
7495// operation. However for setting break points which is normally done through
7496// some kind of user interaction the performance is not crucial.
7497static Handle<Object> Runtime_GetScriptFromScriptName(
7498 Handle<String> script_name) {
7499 // Scan the heap for Script objects to find the script with the requested
7500 // script data.
7501 Handle<Script> script;
7502 HeapIterator iterator;
7503 while (script.is_null() && iterator.has_next()) {
7504 HeapObject* obj = iterator.next();
7505 // If a script is found check if it has the script data requested.
7506 if (obj->IsScript()) {
7507 if (Script::cast(obj)->name()->IsString()) {
7508 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7509 script = Handle<Script>(Script::cast(obj));
7510 }
7511 }
7512 }
7513 }
7514
7515 // If no script with the requested script data is found return undefined.
7516 if (script.is_null()) return Factory::undefined_value();
7517
7518 // Return the script found.
7519 return GetScriptWrapper(script);
7520}
7521
7522
7523// Get the script object from script data. NOTE: Regarding performance
7524// see the NOTE for GetScriptFromScriptData.
7525// args[0]: script data for the script to find the source for
7526static Object* Runtime_GetScript(Arguments args) {
7527 HandleScope scope;
7528
7529 ASSERT(args.length() == 1);
7530
7531 CONVERT_CHECKED(String, script_name, args[0]);
7532
7533 // Find the requested script.
7534 Handle<Object> result =
7535 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7536 return *result;
7537}
7538
7539
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007540// Determines whether the given stack frame should be displayed in
7541// a stack trace. The caller is the error constructor that asked
7542// for the stack trace to be collected. The first time a construct
7543// call to this function is encountered it is skipped. The seen_caller
7544// in/out parameter is used to remember if the caller has been seen
7545// yet.
7546static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7547 bool* seen_caller) {
7548 // Only display JS frames.
7549 if (!raw_frame->is_java_script())
7550 return false;
7551 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7552 Object* raw_fun = frame->function();
7553 // Not sure when this can happen but skip it just in case.
7554 if (!raw_fun->IsJSFunction())
7555 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007556 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007557 *seen_caller = true;
7558 return false;
7559 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007560 // Skip all frames until we've seen the caller. Also, skip the most
7561 // obvious builtin calls. Some builtin calls (such as Number.ADD
7562 // which is invoked using 'call') are very difficult to recognize
7563 // so we're leaving them in for now.
7564 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007565}
7566
7567
7568// Collect the raw data for a stack trace. Returns an array of three
7569// element segments each containing a receiver, function and native
7570// code offset.
7571static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007572 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007573 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007574 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7575
7576 HandleScope scope;
7577
7578 int initial_size = limit < 10 ? limit : 10;
7579 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007580
7581 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007582 // If the caller parameter is a function we skip frames until we're
7583 // under it before starting to collect.
7584 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007585 int cursor = 0;
7586 int frames_seen = 0;
7587 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007588 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007589 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007590 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007591 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007592 Object* recv = frame->receiver();
7593 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007594 Address pc = frame->pc();
7595 Address start = frame->code()->address();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007596 Smi* offset = Smi::FromInt(pc - start);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007597 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007598 if (cursor + 2 < elements->length()) {
7599 elements->set(cursor++, recv);
7600 elements->set(cursor++, fun);
7601 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7602 } else {
7603 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007604 Handle<Object> recv_handle(recv);
7605 Handle<Object> fun_handle(fun);
7606 SetElement(result, cursor++, recv_handle);
7607 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007608 SetElement(result, cursor++, Handle<Smi>(offset));
7609 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007610 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007611 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007612 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007613
7614 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7615
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007616 return *result;
7617}
7618
7619
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007620static Object* Runtime_Abort(Arguments args) {
7621 ASSERT(args.length() == 2);
7622 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7623 Smi::cast(args[1])->value());
7624 Top::PrintStack();
7625 OS::Abort();
7626 UNREACHABLE();
7627 return NULL;
7628}
7629
7630
kasper.lund44510672008-07-25 07:37:58 +00007631#ifdef DEBUG
7632// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7633// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007634static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007635 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007636 HandleScope scope;
7637 Handle<JSArray> result = Factory::NewJSArray(0);
7638 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007639#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007640 { \
7641 HandleScope inner; \
7642 Handle<String> name = \
7643 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7644 Handle<JSArray> pair = Factory::NewJSArray(0); \
7645 SetElement(pair, 0, name); \
7646 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7647 SetElement(result, index++, pair); \
7648 }
7649 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7650#undef ADD_ENTRY
7651 return *result;
7652}
kasper.lund44510672008-07-25 07:37:58 +00007653#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007654
7655
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007656static Object* Runtime_Log(Arguments args) {
7657 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007658 CONVERT_CHECKED(String, format, args[0]);
7659 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007660 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007661 Logger::LogRuntime(chars, elms);
7662 return Heap::undefined_value();
7663}
7664
7665
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007666static Object* Runtime_IS_VAR(Arguments args) {
7667 UNREACHABLE(); // implemented as macro in the parser
7668 return NULL;
7669}
7670
7671
7672// ----------------------------------------------------------------------------
7673// Implementation of Runtime
7674
ager@chromium.orga1645e22009-09-09 19:27:10 +00007675#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007676 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00007677 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007678
7679static Runtime::Function Runtime_functions[] = {
7680 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00007681 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007682};
7683
7684#undef F
7685
7686
7687Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7688 ASSERT(0 <= fid && fid < kNofFunctions);
7689 return &Runtime_functions[fid];
7690}
7691
7692
7693Runtime::Function* Runtime::FunctionForName(const char* name) {
7694 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7695 if (strcmp(f->name, name) == 0) {
7696 return f;
7697 }
7698 }
7699 return NULL;
7700}
7701
7702
7703void Runtime::PerformGC(Object* result) {
7704 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007705 if (failure->IsRetryAfterGC()) {
7706 // Try to do a garbage collection; ignore it if it fails. The C
7707 // entry stub will throw an out-of-memory exception in that case.
7708 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7709 } else {
7710 // Handle last resort GC and make sure to allow future allocations
7711 // to grow the heap without causing GCs (if possible).
7712 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007713 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007714 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007715}
7716
7717
7718} } // namespace v8::internal