blob: 0b981673bb3f3c8e11fa22323690cbf5343f1b98 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
35#include "compiler.h"
36#include "cpu.h"
37#include "dateparser.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
42#include "platform.h"
43#include "runtime.h"
44#include "scopeinfo.h"
45#include "v8threads.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000046#include "smart-pointer.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000047#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048
kasperl@chromium.org71affb52009-05-26 05:44:31 +000049namespace v8 {
50namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051
52
ager@chromium.org3e875802009-06-29 08:26:34 +000053#define RUNTIME_ASSERT(value) \
54 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000055
56// Cast the given object to a value of the specified type and store
57// it in a variable with the given name. If the object is not of the
58// expected type call IllegalOperation and return.
59#define CONVERT_CHECKED(Type, name, obj) \
60 RUNTIME_ASSERT(obj->Is##Type()); \
61 Type* name = Type::cast(obj);
62
63#define CONVERT_ARG_CHECKED(Type, name, index) \
64 RUNTIME_ASSERT(args[index]->Is##Type()); \
65 Handle<Type> name = args.at<Type>(index);
66
kasper.lundbd3ec4e2008-07-09 11:06:54 +000067// Cast the given object to a boolean and store it in a variable with
68// the given name. If the object is not a boolean call IllegalOperation
69// and return.
70#define CONVERT_BOOLEAN_CHECKED(name, obj) \
71 RUNTIME_ASSERT(obj->IsBoolean()); \
72 bool name = (obj)->IsTrue();
73
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000074// Cast the given object to a Smi and store its value in an int variable
75// with the given name. If the object is not a Smi call IllegalOperation
76// and return.
77#define CONVERT_SMI_CHECKED(name, obj) \
78 RUNTIME_ASSERT(obj->IsSmi()); \
79 int name = Smi::cast(obj)->value();
80
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000081// Cast the given object to a double and store it in a variable with
82// the given name. If the object is not a number (as opposed to
83// the number not-a-number) call IllegalOperation and return.
84#define CONVERT_DOUBLE_CHECKED(name, obj) \
85 RUNTIME_ASSERT(obj->IsNumber()); \
86 double name = (obj)->Number();
87
88// Call the specified converter on the object *comand store the result in
89// a variable of the specified type with the given name. If the
90// object is not a Number call IllegalOperation and return.
91#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
92 RUNTIME_ASSERT(obj->IsNumber()); \
93 type name = NumberTo##Type(obj);
94
95// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000096static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097
98
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000099static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
100 StackLimitCheck check;
101 if (check.HasOverflowed()) return Top::StackOverflow();
102
103 Object* result = Heap::CopyJSObject(boilerplate);
104 if (result->IsFailure()) return result;
105 JSObject* copy = JSObject::cast(result);
106
107 // Deep copy local properties.
108 if (copy->HasFastProperties()) {
109 FixedArray* properties = copy->properties();
110 WriteBarrierMode mode = properties->GetWriteBarrierMode();
111 for (int i = 0; i < properties->length(); i++) {
112 Object* value = properties->get(i);
113 if (value->IsJSObject()) {
114 JSObject* jsObject = JSObject::cast(value);
115 result = DeepCopyBoilerplate(jsObject);
116 if (result->IsFailure()) return result;
117 properties->set(i, result, mode);
118 }
119 }
120 mode = copy->GetWriteBarrierMode();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
125 JSObject* jsObject = JSObject::cast(value);
126 result = DeepCopyBoilerplate(jsObject);
127 if (result->IsFailure()) return result;
128 copy->InObjectPropertyAtPut(i, result, mode);
129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
138 String* keyString = String::cast(names->get(i));
139 PropertyAttributes attributes =
140 copy->GetLocalPropertyAttribute(keyString);
141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
145 Object* value = copy->GetProperty(keyString, &attributes);
146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
148 JSObject* jsObject = JSObject::cast(value);
149 result = DeepCopyBoilerplate(jsObject);
150 if (result->IsFailure()) return result;
151 result = copy->SetProperty(keyString, result, NONE);
152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
159 ASSERT(!copy->HasPixelElements());
160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
163 WriteBarrierMode mode = elements->GetWriteBarrierMode();
164 for (int i = 0; i < elements->length(); i++) {
165 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000166 if (value->IsJSObject()) {
167 JSObject* jsObject = JSObject::cast(value);
168 result = DeepCopyBoilerplate(jsObject);
169 if (result->IsFailure()) return result;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000170 elements->set(i, result, mode);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000171 }
172 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000173 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000174 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000175 case JSObject::DICTIONARY_ELEMENTS: {
176 NumberDictionary* element_dictionary = copy->element_dictionary();
177 int capacity = element_dictionary->Capacity();
178 for (int i = 0; i < capacity; i++) {
179 Object* k = element_dictionary->KeyAt(i);
180 if (element_dictionary->IsKey(k)) {
181 Object* value = element_dictionary->ValueAt(i);
182 if (value->IsJSObject()) {
183 JSObject* jsObject = JSObject::cast(value);
184 result = DeepCopyBoilerplate(jsObject);
185 if (result->IsFailure()) return result;
186 element_dictionary->ValueAtPut(i, result);
187 }
188 }
189 }
190 break;
191 }
192 default:
193 UNREACHABLE();
194 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000195 }
196 return copy;
197}
198
199
200static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
201 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
202 return DeepCopyBoilerplate(boilerplate);
203}
204
205
206static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000207 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000208 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000209}
210
211
ager@chromium.org236ad962008-09-25 09:45:57 +0000212static Handle<Map> ComputeObjectLiteralMap(
213 Handle<Context> context,
214 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000215 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000216 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000217 if (FLAG_canonicalize_object_literal_maps) {
218 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000219 int number_of_symbol_keys = 0;
220 while ((number_of_symbol_keys < number_of_properties) &&
221 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
222 number_of_symbol_keys++;
223 }
224 // Based on the number of prefix symbols key we decide whether
225 // to use the map cache in the global context.
226 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000227 if ((number_of_symbol_keys == number_of_properties) &&
228 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000229 // Create the fixed array with the key.
230 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
231 for (int i = 0; i < number_of_symbol_keys; i++) {
232 keys->set(i, constant_properties->get(i*2));
233 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000234 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000235 return Factory::ObjectLiteralMapFromCache(context, keys);
236 }
237 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000238 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000239 return Factory::CopyMap(
240 Handle<Map>(context->object_function()->initial_map()),
241 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000242}
243
244
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000245static Handle<Object> CreateLiteralBoilerplate(
246 Handle<FixedArray> literals,
247 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000248
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000249
250static Handle<Object> CreateObjectLiteralBoilerplate(
251 Handle<FixedArray> literals,
252 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000268 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000269 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000270 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000271 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000272 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 for (int index = 0; index < length; index +=2) {
274 Handle<Object> key(constant_properties->get(index+0));
275 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000276 if (value->IsFixedArray()) {
277 // The value contains the constant_properties of a
278 // simple object literal.
279 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
280 value = CreateLiteralBoilerplate(literals, array);
281 if (value.is_null()) return value;
282 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000283 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284 uint32_t element_index = 0;
285 if (key->IsSymbol()) {
286 // If key is a symbol it is not an array element.
287 Handle<String> name(String::cast(*key));
288 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000289 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290 } else if (Array::IndexFromObject(*key, &element_index)) {
291 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000292 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000293 } else {
294 // Non-uint32 number.
295 ASSERT(key->IsNumber());
296 double num = key->Number();
297 char arr[100];
298 Vector<char> buffer(arr, ARRAY_SIZE(arr));
299 const char* str = DoubleToCString(num, buffer);
300 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000301 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000302 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000303 // If setting the property on the boilerplate throws an
304 // exception, the exception is converted to an empty handle in
305 // the handle based operations. In that case, we need to
306 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000307 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000308 }
309 }
310
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000312}
313
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315static Handle<Object> CreateArrayLiteralBoilerplate(
316 Handle<FixedArray> literals,
317 Handle<FixedArray> elements) {
318 // Create the JSArray.
319 Handle<JSFunction> constructor(
320 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
321 Handle<Object> object = Factory::NewJSObject(constructor);
322
323 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
324
325 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
326 for (int i = 0; i < content->length(); i++) {
327 if (content->get(i)->IsFixedArray()) {
328 // The value contains the constant_properties of a
329 // simple object literal.
330 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
331 Handle<Object> result =
332 CreateLiteralBoilerplate(literals, fa);
333 if (result.is_null()) return result;
334 content->set(i, *result);
335 }
336 }
337
338 // Set the elements.
339 Handle<JSArray>::cast(object)->SetContent(*content);
340 return object;
341}
342
343
344static Handle<Object> CreateLiteralBoilerplate(
345 Handle<FixedArray> literals,
346 Handle<FixedArray> array) {
347 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
348 switch (CompileTimeValue::GetType(array)) {
349 case CompileTimeValue::OBJECT_LITERAL:
350 return CreateObjectLiteralBoilerplate(literals, elements);
351 case CompileTimeValue::ARRAY_LITERAL:
352 return CreateArrayLiteralBoilerplate(literals, elements);
353 default:
354 UNREACHABLE();
355 return Handle<Object>::null();
356 }
357}
358
359
360static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
361 HandleScope scope;
362 ASSERT(args.length() == 3);
363 // Copy the arguments.
364 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
365 CONVERT_SMI_CHECKED(literals_index, args[1]);
366 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
367
368 Handle<Object> result =
369 CreateObjectLiteralBoilerplate(literals, constant_properties);
370
371 if (result.is_null()) return Failure::Exception();
372
373 // Update the functions literal and return the boilerplate.
374 literals->set(literals_index, *result);
375
376 return *result;
377}
378
379
380static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000381 // Takes a FixedArray of elements containing the literal elements of
382 // the array literal and produces JSArray with those elements.
383 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000384 // which contains the context from which to get the Array function
385 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000386 HandleScope scope;
387 ASSERT(args.length() == 3);
388 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
389 CONVERT_SMI_CHECKED(literals_index, args[1]);
390 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000392 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
393 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000394
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000395 // Update the functions literal and return the boilerplate.
396 literals->set(literals_index, *object);
397 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000398}
399
400
ager@chromium.org32912102009-01-16 10:38:43 +0000401static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
402 ASSERT(args.length() == 2);
403 CONVERT_CHECKED(String, key, args[0]);
404 Object* value = args[1];
405 // Create a catch context extension object.
406 JSFunction* constructor =
407 Top::context()->global_context()->context_extension_function();
408 Object* object = Heap::AllocateJSObject(constructor);
409 if (object->IsFailure()) return object;
410 // Assign the exception value to the catch variable and make sure
411 // that the catch variable is DontDelete.
412 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
413 if (value->IsFailure()) return value;
414 return object;
415}
416
417
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000418static Object* Runtime_ClassOf(Arguments args) {
419 NoHandleAllocation ha;
420 ASSERT(args.length() == 1);
421 Object* obj = args[0];
422 if (!obj->IsJSObject()) return Heap::null_value();
423 return JSObject::cast(obj)->class_name();
424}
425
ager@chromium.org7c537e22008-10-16 08:43:32 +0000426
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000427static Object* Runtime_IsInPrototypeChain(Arguments args) {
428 NoHandleAllocation ha;
429 ASSERT(args.length() == 2);
430 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
431 Object* O = args[0];
432 Object* V = args[1];
433 while (true) {
434 Object* prototype = V->GetPrototype();
435 if (prototype->IsNull()) return Heap::false_value();
436 if (O == prototype) return Heap::true_value();
437 V = prototype;
438 }
439}
440
441
ager@chromium.org9085a012009-05-11 19:22:57 +0000442// Inserts an object as the hidden prototype of another object.
443static Object* Runtime_SetHiddenPrototype(Arguments args) {
444 NoHandleAllocation ha;
445 ASSERT(args.length() == 2);
446 CONVERT_CHECKED(JSObject, jsobject, args[0]);
447 CONVERT_CHECKED(JSObject, proto, args[1]);
448
449 // Sanity checks. The old prototype (that we are replacing) could
450 // theoretically be null, but if it is not null then check that we
451 // didn't already install a hidden prototype here.
452 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
453 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
454 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
455
456 // Allocate up front before we start altering state in case we get a GC.
457 Object* map_or_failure = proto->map()->CopyDropTransitions();
458 if (map_or_failure->IsFailure()) return map_or_failure;
459 Map* new_proto_map = Map::cast(map_or_failure);
460
461 map_or_failure = jsobject->map()->CopyDropTransitions();
462 if (map_or_failure->IsFailure()) return map_or_failure;
463 Map* new_map = Map::cast(map_or_failure);
464
465 // Set proto's prototype to be the old prototype of the object.
466 new_proto_map->set_prototype(jsobject->GetPrototype());
467 proto->set_map(new_proto_map);
468 new_proto_map->set_is_hidden_prototype();
469
470 // Set the object's prototype to proto.
471 new_map->set_prototype(proto);
472 jsobject->set_map(new_map);
473
474 return Heap::undefined_value();
475}
476
477
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000478static Object* Runtime_IsConstructCall(Arguments args) {
479 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000480 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000481 JavaScriptFrameIterator it;
482 return Heap::ToBoolean(it.frame()->IsConstructor());
483}
484
485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000486static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000487 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000489 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
490 CONVERT_ARG_CHECKED(String, pattern, 1);
491 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000492 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
493 if (result.is_null()) return Failure::Exception();
494 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000495}
496
497
498static Object* Runtime_CreateApiFunction(Arguments args) {
499 HandleScope scope;
500 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000501 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000502 return *Factory::CreateApiFunction(data);
503}
504
505
506static Object* Runtime_IsTemplate(Arguments args) {
507 ASSERT(args.length() == 1);
508 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000509 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000510 return Heap::ToBoolean(result);
511}
512
513
514static Object* Runtime_GetTemplateField(Arguments args) {
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000517 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000518 int index = field->value();
519 int offset = index * kPointerSize + HeapObject::kHeaderSize;
520 InstanceType type = templ->map()->instance_type();
521 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
522 type == OBJECT_TEMPLATE_INFO_TYPE);
523 RUNTIME_ASSERT(offset > 0);
524 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
525 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
526 } else {
527 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
528 }
529 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000530}
531
532
ager@chromium.org870a0b62008-11-04 11:43:05 +0000533static Object* Runtime_DisableAccessChecks(Arguments args) {
534 ASSERT(args.length() == 1);
535 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000536 Map* old_map = object->map();
537 bool needs_access_checks = old_map->is_access_check_needed();
538 if (needs_access_checks) {
539 // Copy map so it won't interfere constructor's initial map.
540 Object* new_map = old_map->CopyDropTransitions();
541 if (new_map->IsFailure()) return new_map;
542
543 Map::cast(new_map)->set_is_access_check_needed(false);
544 object->set_map(Map::cast(new_map));
545 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000546 return needs_access_checks ? Heap::true_value() : Heap::false_value();
547}
548
549
550static Object* Runtime_EnableAccessChecks(Arguments args) {
551 ASSERT(args.length() == 1);
552 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000553 Map* old_map = object->map();
554 if (!old_map->is_access_check_needed()) {
555 // Copy map so it won't interfere constructor's initial map.
556 Object* new_map = old_map->CopyDropTransitions();
557 if (new_map->IsFailure()) return new_map;
558
559 Map::cast(new_map)->set_is_access_check_needed(true);
560 object->set_map(Map::cast(new_map));
561 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000562 return Heap::undefined_value();
563}
564
565
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000566static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
567 HandleScope scope;
568 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
569 Handle<Object> args[2] = { type_handle, name };
570 Handle<Object> error =
571 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
572 return Top::Throw(*error);
573}
574
575
576static Object* Runtime_DeclareGlobals(Arguments args) {
577 HandleScope scope;
578 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
579
580 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
581 Handle<Context> context = args.at<Context>(1);
582 bool is_eval = Smi::cast(args[2])->value() == 1;
583
584 // Compute the property attributes. According to ECMA-262, section
585 // 13, page 71, the property must be read-only and
586 // non-deletable. However, neither SpiderMonkey nor KJS creates the
587 // property as read-only, so we don't either.
588 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
589
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000590 // Traverse the name/value pairs and set the properties.
591 int length = pairs->length();
592 for (int i = 0; i < length; i += 2) {
593 HandleScope scope;
594 Handle<String> name(String::cast(pairs->get(i)));
595 Handle<Object> value(pairs->get(i + 1));
596
597 // We have to declare a global const property. To capture we only
598 // assign to it when evaluating the assignment for "const x =
599 // <expr>" the initial value is the hole.
600 bool is_const_property = value->IsTheHole();
601
602 if (value->IsUndefined() || is_const_property) {
603 // Lookup the property in the global object, and don't set the
604 // value of the variable if the property is already there.
605 LookupResult lookup;
606 global->Lookup(*name, &lookup);
607 if (lookup.IsProperty()) {
608 // Determine if the property is local by comparing the holder
609 // against the global object. The information will be used to
610 // avoid throwing re-declaration errors when declaring
611 // variables or constants that exist in the prototype chain.
612 bool is_local = (*global == lookup.holder());
613 // Get the property attributes and determine if the property is
614 // read-only.
615 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
616 bool is_read_only = (attributes & READ_ONLY) != 0;
617 if (lookup.type() == INTERCEPTOR) {
618 // If the interceptor says the property is there, we
619 // just return undefined without overwriting the property.
620 // Otherwise, we continue to setting the property.
621 if (attributes != ABSENT) {
622 // Check if the existing property conflicts with regards to const.
623 if (is_local && (is_read_only || is_const_property)) {
624 const char* type = (is_read_only) ? "const" : "var";
625 return ThrowRedeclarationError(type, name);
626 };
627 // The property already exists without conflicting: Go to
628 // the next declaration.
629 continue;
630 }
631 // Fall-through and introduce the absent property by using
632 // SetProperty.
633 } else {
634 if (is_local && (is_read_only || is_const_property)) {
635 const char* type = (is_read_only) ? "const" : "var";
636 return ThrowRedeclarationError(type, name);
637 }
638 // The property already exists without conflicting: Go to
639 // the next declaration.
640 continue;
641 }
642 }
643 } else {
644 // Copy the function and update its context. Use it as value.
645 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
646 Handle<JSFunction> function =
647 Factory::NewFunctionFromBoilerplate(boilerplate, context);
648 value = function;
649 }
650
651 LookupResult lookup;
652 global->LocalLookup(*name, &lookup);
653
654 PropertyAttributes attributes = is_const_property
655 ? static_cast<PropertyAttributes>(base | READ_ONLY)
656 : base;
657
658 if (lookup.IsProperty()) {
659 // There's a local property that we need to overwrite because
660 // we're either declaring a function or there's an interceptor
661 // that claims the property is absent.
662
663 // Check for conflicting re-declarations. We cannot have
664 // conflicting types in case of intercepted properties because
665 // they are absent.
666 if (lookup.type() != INTERCEPTOR &&
667 (lookup.IsReadOnly() || is_const_property)) {
668 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
669 return ThrowRedeclarationError(type, name);
670 }
671 SetProperty(global, name, value, attributes);
672 } else {
673 // If a property with this name does not already exist on the
674 // global object add the property locally. We take special
675 // precautions to always add it as a local property even in case
676 // of callbacks in the prototype chain (this rules out using
677 // SetProperty). Also, we must use the handle-based version to
678 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000679 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000680 }
681 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000683 return Heap::undefined_value();
684}
685
686
687static Object* Runtime_DeclareContextSlot(Arguments args) {
688 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000689 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000690
ager@chromium.org7c537e22008-10-16 08:43:32 +0000691 CONVERT_ARG_CHECKED(Context, context, 0);
692 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000693 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000694 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000696 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697
698 // Declarations are always done in the function context.
699 context = Handle<Context>(context->fcontext());
700
701 int index;
702 PropertyAttributes attributes;
703 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000704 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 context->Lookup(name, flags, &index, &attributes);
706
707 if (attributes != ABSENT) {
708 // The name was declared before; check for conflicting
709 // re-declarations: This is similar to the code in parser.cc in
710 // the AstBuildingParser::Declare function.
711 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
712 // Functions are not read-only.
713 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
714 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
715 return ThrowRedeclarationError(type, name);
716 }
717
718 // Initialize it if necessary.
719 if (*initial_value != NULL) {
720 if (index >= 0) {
721 // The variable or constant context slot should always be in
722 // the function context; not in any outer context nor in the
723 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000724 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725 if (((attributes & READ_ONLY) == 0) ||
726 context->get(index)->IsTheHole()) {
727 context->set(index, *initial_value);
728 }
729 } else {
730 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000731 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000732 SetProperty(context_ext, name, initial_value, mode);
733 }
734 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000737 // The property is not in the function context. It needs to be
738 // "declared" in the function context's extension context, or in the
739 // global context.
740 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000741 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000742 // The function context's extension context exists - use it.
743 context_ext = Handle<JSObject>(context->extension());
744 } else {
745 // The function context's extension context does not exists - allocate
746 // it.
747 context_ext = Factory::NewJSObject(Top::context_extension_function());
748 // And store it in the extension slot.
749 context->set_extension(*context_ext);
750 }
751 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000752
ager@chromium.org7c537e22008-10-16 08:43:32 +0000753 // Declare the property by setting it to the initial value if provided,
754 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
755 // constant declarations).
756 ASSERT(!context_ext->HasLocalProperty(*name));
757 Handle<Object> value(Heap::undefined_value());
758 if (*initial_value != NULL) value = initial_value;
759 SetProperty(context_ext, name, value, mode);
760 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
761 }
762
763 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000764}
765
766
767static Object* Runtime_InitializeVarGlobal(Arguments args) {
768 NoHandleAllocation nha;
769
770 // Determine if we need to assign to the variable if it already
771 // exists (based on the number of arguments).
772 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
773 bool assign = args.length() == 2;
774
775 CONVERT_ARG_CHECKED(String, name, 0);
776 GlobalObject* global = Top::context()->global();
777
778 // According to ECMA-262, section 12.2, page 62, the property must
779 // not be deletable.
780 PropertyAttributes attributes = DONT_DELETE;
781
782 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000783 // there, there is a property with this name in the prototype chain.
784 // We follow Safari and Firefox behavior and only set the property
785 // locally if there is an explicit initialization value that we have
786 // to assign to the property. When adding the property we take
787 // special precautions to always add it as a local property even in
788 // case of callbacks in the prototype chain (this rules out using
789 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
790 // this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000791 LookupResult lookup;
792 global->LocalLookup(*name, &lookup);
793 if (!lookup.IsProperty()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000794 if (assign) {
795 return global->IgnoreAttributesAndSetLocalProperty(*name,
796 args[1],
797 attributes);
798 }
799 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000800 }
801
802 // Determine if this is a redeclaration of something read-only.
803 if (lookup.IsReadOnly()) {
804 return ThrowRedeclarationError("const", name);
805 }
806
807 // Determine if this is a redeclaration of an intercepted read-only
808 // property and figure out if the property exists at all.
809 bool found = true;
810 PropertyType type = lookup.type();
811 if (type == INTERCEPTOR) {
812 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
813 if (intercepted == ABSENT) {
814 // The interceptor claims the property isn't there. We need to
815 // make sure to introduce it.
816 found = false;
817 } else if ((intercepted & READ_ONLY) != 0) {
818 // The property is present, but read-only. Since we're trying to
819 // overwrite it with a variable declaration we must throw a
820 // re-declaration error.
821 return ThrowRedeclarationError("const", name);
822 }
823 // Restore global object from context (in case of GC).
824 global = Top::context()->global();
825 }
826
827 if (found && !assign) {
828 // The global property is there and we're not assigning any value
829 // to it. Just return.
830 return Heap::undefined_value();
831 }
832
833 // Assign the value (or undefined) to the property.
834 Object* value = (assign) ? args[1] : Heap::undefined_value();
835 return global->SetProperty(&lookup, *name, value, attributes);
836}
837
838
839static Object* Runtime_InitializeConstGlobal(Arguments args) {
840 // All constants are declared with an initial value. The name
841 // of the constant is the first argument and the initial value
842 // is the second.
843 RUNTIME_ASSERT(args.length() == 2);
844 CONVERT_ARG_CHECKED(String, name, 0);
845 Handle<Object> value = args.at<Object>(1);
846
847 // Get the current global object from top.
848 GlobalObject* global = Top::context()->global();
849
850 // According to ECMA-262, section 12.2, page 62, the property must
851 // not be deletable. Since it's a const, it must be READ_ONLY too.
852 PropertyAttributes attributes =
853 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
854
855 // Lookup the property locally in the global object. If it isn't
856 // there, we add the property and take special precautions to always
857 // add it as a local property even in case of callbacks in the
858 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000859 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860 LookupResult lookup;
861 global->LocalLookup(*name, &lookup);
862 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000863 return global->IgnoreAttributesAndSetLocalProperty(*name,
864 *value,
865 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000866 }
867
868 // Determine if this is a redeclaration of something not
869 // read-only. In case the result is hidden behind an interceptor we
870 // need to ask it for the property attributes.
871 if (!lookup.IsReadOnly()) {
872 if (lookup.type() != INTERCEPTOR) {
873 return ThrowRedeclarationError("var", name);
874 }
875
876 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
877
878 // Throw re-declaration error if the intercepted property is present
879 // but not read-only.
880 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
881 return ThrowRedeclarationError("var", name);
882 }
883
884 // Restore global object from context (in case of GC) and continue
885 // with setting the value because the property is either absent or
886 // read-only. We also have to do redo the lookup.
887 global = Top::context()->global();
888
889 // BUG 1213579: Handle the case where we have to set a read-only
890 // property through an interceptor and only do it if it's
891 // uninitialized, e.g. the hole. Nirk...
892 global->SetProperty(*name, *value, attributes);
893 return *value;
894 }
895
896 // Set the value, but only we're assigning the initial value to a
897 // constant. For now, we determine this by checking if the
898 // current value is the hole.
899 PropertyType type = lookup.type();
900 if (type == FIELD) {
901 FixedArray* properties = global->properties();
902 int index = lookup.GetFieldIndex();
903 if (properties->get(index)->IsTheHole()) {
904 properties->set(index, *value);
905 }
906 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000907 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
908 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 }
910 } else {
911 // Ignore re-initialization of constants that have already been
912 // assigned a function value.
913 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
914 }
915
916 // Use the set value as the result of the operation.
917 return *value;
918}
919
920
921static Object* Runtime_InitializeConstContextSlot(Arguments args) {
922 HandleScope scope;
923 ASSERT(args.length() == 3);
924
925 Handle<Object> value(args[0]);
926 ASSERT(!value->IsTheHole());
927 CONVERT_ARG_CHECKED(Context, context, 1);
928 Handle<String> name(String::cast(args[2]));
929
930 // Initializations are always done in the function context.
931 context = Handle<Context>(context->fcontext());
932
933 int index;
934 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000935 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000936 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000937 context->Lookup(name, flags, &index, &attributes);
938
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000939 // In most situations, the property introduced by the const
940 // declaration should be present in the context extension object.
941 // However, because declaration and initialization are separate, the
942 // property might have been deleted (if it was introduced by eval)
943 // before we reach the initialization point.
944 //
945 // Example:
946 //
947 // function f() { eval("delete x; const x;"); }
948 //
949 // In that case, the initialization behaves like a normal assignment
950 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000951 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000952 // Property was found in a context.
953 if (holder->IsContext()) {
954 // The holder cannot be the function context. If it is, there
955 // should have been a const redeclaration error when declaring
956 // the const property.
957 ASSERT(!holder.is_identical_to(context));
958 if ((attributes & READ_ONLY) == 0) {
959 Handle<Context>::cast(holder)->set(index, *value);
960 }
961 } else {
962 // The holder is an arguments object.
963 ASSERT((attributes & READ_ONLY) == 0);
964 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000965 }
966 return *value;
967 }
968
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000969 // The property could not be found, we introduce it in the global
970 // context.
971 if (attributes == ABSENT) {
972 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
973 SetProperty(global, name, value, NONE);
974 return *value;
975 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000976
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000977 // The property was present in a context extension object.
978 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000979
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000980 if (*context_ext == context->extension()) {
981 // This is the property that was introduced by the const
982 // declaration. Set it if it hasn't been set before. NOTE: We
983 // cannot use GetProperty() to get the current value as it
984 // 'unholes' the value.
985 LookupResult lookup;
986 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
987 ASSERT(lookup.IsProperty()); // the property was declared
988 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
989
990 PropertyType type = lookup.type();
991 if (type == FIELD) {
992 FixedArray* properties = context_ext->properties();
993 int index = lookup.GetFieldIndex();
994 if (properties->get(index)->IsTheHole()) {
995 properties->set(index, *value);
996 }
997 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000998 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
999 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001000 }
1001 } else {
1002 // We should not reach here. Any real, named property should be
1003 // either a field or a dictionary slot.
1004 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
1006 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001007 // The property was found in a different context extension object.
1008 // Set it if it is not a read-only property.
1009 if ((attributes & READ_ONLY) == 0) {
1010 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1011 // Setting a property might throw an exception. Exceptions
1012 // are converted to empty handles in handle operations. We
1013 // need to convert back to exceptions here.
1014 if (set.is_null()) {
1015 ASSERT(Top::has_pending_exception());
1016 return Failure::Exception();
1017 }
1018 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001019 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001020
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001021 return *value;
1022}
1023
1024
1025static Object* Runtime_RegExpExec(Arguments args) {
1026 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001027 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001028 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1029 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001030 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001031 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001032 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001033 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001034 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001035 RUNTIME_ASSERT(index >= 0);
1036 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001037 Handle<Object> result = RegExpImpl::Exec(regexp,
1038 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001039 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001040 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001041 if (result.is_null()) return Failure::Exception();
1042 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001043}
1044
1045
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001046static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1047 HandleScope scope;
1048 ASSERT(args.length() == 4);
1049 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1050 int index = Smi::cast(args[1])->value();
1051 Handle<String> pattern = args.at<String>(2);
1052 Handle<String> flags = args.at<String>(3);
1053
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001054 // Get the RegExp function from the context in the literals array.
1055 // This is the RegExp function from the context in which the
1056 // function was created. We do not use the RegExp function from the
1057 // current global context because this might be the RegExp function
1058 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001059 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001060 Handle<JSFunction>(
1061 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001062 // Compute the regular expression literal.
1063 bool has_pending_exception;
1064 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001065 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1066 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001067 if (has_pending_exception) {
1068 ASSERT(Top::has_pending_exception());
1069 return Failure::Exception();
1070 }
1071 literals->set(index, *regexp);
1072 return *regexp;
1073}
1074
1075
1076static Object* Runtime_FunctionGetName(Arguments args) {
1077 NoHandleAllocation ha;
1078 ASSERT(args.length() == 1);
1079
1080 CONVERT_CHECKED(JSFunction, f, args[0]);
1081 return f->shared()->name();
1082}
1083
1084
ager@chromium.org236ad962008-09-25 09:45:57 +00001085static Object* Runtime_FunctionSetName(Arguments args) {
1086 NoHandleAllocation ha;
1087 ASSERT(args.length() == 2);
1088
1089 CONVERT_CHECKED(JSFunction, f, args[0]);
1090 CONVERT_CHECKED(String, name, args[1]);
1091 f->shared()->set_name(name);
1092 return Heap::undefined_value();
1093}
1094
1095
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001096static Object* Runtime_FunctionGetScript(Arguments args) {
1097 HandleScope scope;
1098 ASSERT(args.length() == 1);
1099
1100 CONVERT_CHECKED(JSFunction, fun, args[0]);
1101 Handle<Object> script = Handle<Object>(fun->shared()->script());
1102 if (!script->IsScript()) return Heap::undefined_value();
1103
1104 return *GetScriptWrapper(Handle<Script>::cast(script));
1105}
1106
1107
1108static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1109 NoHandleAllocation ha;
1110 ASSERT(args.length() == 1);
1111
1112 CONVERT_CHECKED(JSFunction, f, args[0]);
1113 return f->shared()->GetSourceCode();
1114}
1115
1116
1117static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1118 NoHandleAllocation ha;
1119 ASSERT(args.length() == 1);
1120
1121 CONVERT_CHECKED(JSFunction, fun, args[0]);
1122 int pos = fun->shared()->start_position();
1123 return Smi::FromInt(pos);
1124}
1125
1126
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001127static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1128 ASSERT(args.length() == 2);
1129
1130 CONVERT_CHECKED(JSFunction, fun, args[0]);
1131 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1132
1133 Code* code = fun->code();
1134 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1135
1136 Address pc = code->address() + offset;
1137 return Smi::FromInt(fun->code()->SourcePosition(pc));
1138}
1139
1140
1141
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001142static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1143 NoHandleAllocation ha;
1144 ASSERT(args.length() == 2);
1145
1146 CONVERT_CHECKED(JSFunction, fun, args[0]);
1147 CONVERT_CHECKED(String, name, args[1]);
1148 fun->SetInstanceClassName(name);
1149 return Heap::undefined_value();
1150}
1151
1152
1153static Object* Runtime_FunctionSetLength(Arguments args) {
1154 NoHandleAllocation ha;
1155 ASSERT(args.length() == 2);
1156
1157 CONVERT_CHECKED(JSFunction, fun, args[0]);
1158 CONVERT_CHECKED(Smi, length, args[1]);
1159 fun->shared()->set_length(length->value());
1160 return length;
1161}
1162
1163
1164static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001165 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001166 ASSERT(args.length() == 2);
1167
1168 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001169 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1170 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171 return args[0]; // return TOS
1172}
1173
1174
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001175static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1176 NoHandleAllocation ha;
1177 ASSERT(args.length() == 1);
1178
1179 CONVERT_CHECKED(JSFunction, f, args[0]);
1180 // The function_data field of the shared function info is used exclusively by
1181 // the API.
1182 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1183 : Heap::false_value();
1184}
1185
1186
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001187static Object* Runtime_SetCode(Arguments args) {
1188 HandleScope scope;
1189 ASSERT(args.length() == 2);
1190
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001191 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 Handle<Object> code = args.at<Object>(1);
1193
1194 Handle<Context> context(target->context());
1195
1196 if (!code->IsNull()) {
1197 RUNTIME_ASSERT(code->IsJSFunction());
1198 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1199 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1200 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1201 return Failure::Exception();
1202 }
1203 // Set the code, formal parameter count, and the length of the target
1204 // function.
1205 target->set_code(fun->code());
1206 target->shared()->set_length(fun->shared()->length());
1207 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001208 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001209 // Set the source code of the target function to undefined.
1210 // SetCode is only used for built-in constructors like String,
1211 // Array, and Object, and some web code
1212 // doesn't like seeing source code for constructors.
1213 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001214 context = Handle<Context>(fun->context());
1215
1216 // Make sure we get a fresh copy of the literal vector to avoid
1217 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001218 int number_of_literals = fun->NumberOfLiterals();
1219 Handle<FixedArray> literals =
1220 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001221 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001222 // Insert the object, regexp and array functions in the literals
1223 // array prefix. These are the functions that will be used when
1224 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001225 literals->set(JSFunction::kLiteralGlobalContextIndex,
1226 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001227 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001228 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001229 }
1230
1231 target->set_context(*context);
1232 return *target;
1233}
1234
1235
1236static Object* CharCodeAt(String* subject, Object* index) {
1237 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001238 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001239 // Flatten the string. If someone wants to get a char at an index
1240 // in a cons string, it is likely that more indices will be
1241 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001242 subject->TryFlattenIfNotFlat();
1243 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001244 return Heap::nan_value();
1245 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001246 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001247}
1248
1249
1250static Object* Runtime_StringCharCodeAt(Arguments args) {
1251 NoHandleAllocation ha;
1252 ASSERT(args.length() == 2);
1253
1254 CONVERT_CHECKED(String, subject, args[0]);
1255 Object* index = args[1];
1256 return CharCodeAt(subject, index);
1257}
1258
1259
1260static Object* Runtime_CharFromCode(Arguments args) {
1261 NoHandleAllocation ha;
1262 ASSERT(args.length() == 1);
1263 uint32_t code;
1264 if (Array::IndexFromObject(args[0], &code)) {
1265 if (code <= 0xffff) {
1266 return Heap::LookupSingleCharacterStringFromCode(code);
1267 }
1268 }
1269 return Heap::empty_string();
1270}
1271
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001272// Forward declarations.
1273static const int kStringBuilderConcatHelperLengthBits = 11;
1274static const int kStringBuilderConcatHelperPositionBits = 19;
1275
1276template <typename schar>
1277static inline void StringBuilderConcatHelper(String*,
1278 schar*,
1279 FixedArray*,
1280 int);
1281
1282typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1283typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1284
1285class ReplacementStringBuilder {
1286 public:
1287 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1288 : subject_(subject),
1289 parts_(Factory::NewFixedArray(estimated_part_count)),
1290 part_count_(0),
1291 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001292 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001293 // Require a non-zero initial size. Ensures that doubling the size to
1294 // extend the array will work.
1295 ASSERT(estimated_part_count > 0);
1296 }
1297
1298 void EnsureCapacity(int elements) {
1299 int length = parts_->length();
1300 int required_length = part_count_ + elements;
1301 if (length < required_length) {
1302 int new_length = length;
1303 do {
1304 new_length *= 2;
1305 } while (new_length < required_length);
1306 Handle<FixedArray> extended_array =
1307 Factory::NewFixedArray(new_length);
1308 parts_->CopyTo(0, *extended_array, 0, part_count_);
1309 parts_ = extended_array;
1310 }
1311 }
1312
1313 void AddSubjectSlice(int from, int to) {
1314 ASSERT(from >= 0);
1315 int length = to - from;
1316 ASSERT(length > 0);
1317 // Can we encode the slice in 11 bits for length and 19 bits for
1318 // start position - as used by StringBuilderConcatHelper?
1319 if (StringBuilderSubstringLength::is_valid(length) &&
1320 StringBuilderSubstringPosition::is_valid(from)) {
1321 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1322 StringBuilderSubstringPosition::encode(from);
1323 AddElement(Smi::FromInt(encoded_slice));
1324 } else {
1325 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1326 AddElement(*slice);
1327 }
1328 IncrementCharacterCount(length);
1329 }
1330
1331
1332 void AddString(Handle<String> string) {
1333 int length = string->length();
1334 ASSERT(length > 0);
1335 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001336 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001337 is_ascii_ = false;
1338 }
1339 IncrementCharacterCount(length);
1340 }
1341
1342
1343 Handle<String> ToString() {
1344 if (part_count_ == 0) {
1345 return Factory::empty_string();
1346 }
1347
1348 Handle<String> joined_string;
1349 if (is_ascii_) {
1350 joined_string = NewRawAsciiString(character_count_);
1351 AssertNoAllocation no_alloc;
1352 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1353 char* char_buffer = seq->GetChars();
1354 StringBuilderConcatHelper(*subject_,
1355 char_buffer,
1356 *parts_,
1357 part_count_);
1358 } else {
1359 // Non-ASCII.
1360 joined_string = NewRawTwoByteString(character_count_);
1361 AssertNoAllocation no_alloc;
1362 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1363 uc16* char_buffer = seq->GetChars();
1364 StringBuilderConcatHelper(*subject_,
1365 char_buffer,
1366 *parts_,
1367 part_count_);
1368 }
1369 return joined_string;
1370 }
1371
1372
1373 void IncrementCharacterCount(int by) {
1374 if (character_count_ > Smi::kMaxValue - by) {
1375 V8::FatalProcessOutOfMemory("String.replace result too large.");
1376 }
1377 character_count_ += by;
1378 }
1379
1380 private:
1381
1382 Handle<String> NewRawAsciiString(int size) {
1383 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1384 }
1385
1386
1387 Handle<String> NewRawTwoByteString(int size) {
1388 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1389 }
1390
1391
1392 void AddElement(Object* element) {
1393 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001394 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001395 parts_->set(part_count_, element);
1396 part_count_++;
1397 }
1398
1399 Handle<String> subject_;
1400 Handle<FixedArray> parts_;
1401 int part_count_;
1402 int character_count_;
1403 bool is_ascii_;
1404};
1405
1406
1407class CompiledReplacement {
1408 public:
1409 CompiledReplacement()
1410 : parts_(1), replacement_substrings_(0) {}
1411
1412 void Compile(Handle<String> replacement,
1413 int capture_count,
1414 int subject_length);
1415
1416 void Apply(ReplacementStringBuilder* builder,
1417 int match_from,
1418 int match_to,
1419 Handle<JSArray> last_match_info);
1420
1421 // Number of distinct parts of the replacement pattern.
1422 int parts() {
1423 return parts_.length();
1424 }
1425 private:
1426 enum PartType {
1427 SUBJECT_PREFIX = 1,
1428 SUBJECT_SUFFIX,
1429 SUBJECT_CAPTURE,
1430 REPLACEMENT_SUBSTRING,
1431 REPLACEMENT_STRING,
1432
1433 NUMBER_OF_PART_TYPES
1434 };
1435
1436 struct ReplacementPart {
1437 static inline ReplacementPart SubjectMatch() {
1438 return ReplacementPart(SUBJECT_CAPTURE, 0);
1439 }
1440 static inline ReplacementPart SubjectCapture(int capture_index) {
1441 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1442 }
1443 static inline ReplacementPart SubjectPrefix() {
1444 return ReplacementPart(SUBJECT_PREFIX, 0);
1445 }
1446 static inline ReplacementPart SubjectSuffix(int subject_length) {
1447 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1448 }
1449 static inline ReplacementPart ReplacementString() {
1450 return ReplacementPart(REPLACEMENT_STRING, 0);
1451 }
1452 static inline ReplacementPart ReplacementSubString(int from, int to) {
1453 ASSERT(from >= 0);
1454 ASSERT(to > from);
1455 return ReplacementPart(-from, to);
1456 }
1457
1458 // If tag <= 0 then it is the negation of a start index of a substring of
1459 // the replacement pattern, otherwise it's a value from PartType.
1460 ReplacementPart(int tag, int data)
1461 : tag(tag), data(data) {
1462 // Must be non-positive or a PartType value.
1463 ASSERT(tag < NUMBER_OF_PART_TYPES);
1464 }
1465 // Either a value of PartType or a non-positive number that is
1466 // the negation of an index into the replacement string.
1467 int tag;
1468 // The data value's interpretation depends on the value of tag:
1469 // tag == SUBJECT_PREFIX ||
1470 // tag == SUBJECT_SUFFIX: data is unused.
1471 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1472 // tag == REPLACEMENT_SUBSTRING ||
1473 // tag == REPLACEMENT_STRING: data is index into array of substrings
1474 // of the replacement string.
1475 // tag <= 0: Temporary representation of the substring of the replacement
1476 // string ranging over -tag .. data.
1477 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1478 // substring objects.
1479 int data;
1480 };
1481
1482 template<typename Char>
1483 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1484 Vector<Char> characters,
1485 int capture_count,
1486 int subject_length) {
1487 int length = characters.length();
1488 int last = 0;
1489 for (int i = 0; i < length; i++) {
1490 Char c = characters[i];
1491 if (c == '$') {
1492 int next_index = i + 1;
1493 if (next_index == length) { // No next character!
1494 break;
1495 }
1496 Char c2 = characters[next_index];
1497 switch (c2) {
1498 case '$':
1499 if (i > last) {
1500 // There is a substring before. Include the first "$".
1501 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1502 last = next_index + 1; // Continue after the second "$".
1503 } else {
1504 // Let the next substring start with the second "$".
1505 last = next_index;
1506 }
1507 i = next_index;
1508 break;
1509 case '`':
1510 if (i > last) {
1511 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1512 }
1513 parts->Add(ReplacementPart::SubjectPrefix());
1514 i = next_index;
1515 last = i + 1;
1516 break;
1517 case '\'':
1518 if (i > last) {
1519 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1520 }
1521 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1522 i = next_index;
1523 last = i + 1;
1524 break;
1525 case '&':
1526 if (i > last) {
1527 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1528 }
1529 parts->Add(ReplacementPart::SubjectMatch());
1530 i = next_index;
1531 last = i + 1;
1532 break;
1533 case '0':
1534 case '1':
1535 case '2':
1536 case '3':
1537 case '4':
1538 case '5':
1539 case '6':
1540 case '7':
1541 case '8':
1542 case '9': {
1543 int capture_ref = c2 - '0';
1544 if (capture_ref > capture_count) {
1545 i = next_index;
1546 continue;
1547 }
1548 int second_digit_index = next_index + 1;
1549 if (second_digit_index < length) {
1550 // Peek ahead to see if we have two digits.
1551 Char c3 = characters[second_digit_index];
1552 if ('0' <= c3 && c3 <= '9') { // Double digits.
1553 int double_digit_ref = capture_ref * 10 + c3 - '0';
1554 if (double_digit_ref <= capture_count) {
1555 next_index = second_digit_index;
1556 capture_ref = double_digit_ref;
1557 }
1558 }
1559 }
1560 if (capture_ref > 0) {
1561 if (i > last) {
1562 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1563 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001564 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001565 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1566 last = next_index + 1;
1567 }
1568 i = next_index;
1569 break;
1570 }
1571 default:
1572 i = next_index;
1573 break;
1574 }
1575 }
1576 }
1577 if (length > last) {
1578 if (last == 0) {
1579 parts->Add(ReplacementPart::ReplacementString());
1580 } else {
1581 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1582 }
1583 }
1584 }
1585
1586 ZoneList<ReplacementPart> parts_;
1587 ZoneList<Handle<String> > replacement_substrings_;
1588};
1589
1590
1591void CompiledReplacement::Compile(Handle<String> replacement,
1592 int capture_count,
1593 int subject_length) {
1594 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001595 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001596 AssertNoAllocation no_alloc;
1597 ParseReplacementPattern(&parts_,
1598 replacement->ToAsciiVector(),
1599 capture_count,
1600 subject_length);
1601 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001602 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001603 AssertNoAllocation no_alloc;
1604
1605 ParseReplacementPattern(&parts_,
1606 replacement->ToUC16Vector(),
1607 capture_count,
1608 subject_length);
1609 }
1610 // Find substrings of replacement string and create them as String objects..
1611 int substring_index = 0;
1612 for (int i = 0, n = parts_.length(); i < n; i++) {
1613 int tag = parts_[i].tag;
1614 if (tag <= 0) { // A replacement string slice.
1615 int from = -tag;
1616 int to = parts_[i].data;
1617 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1618 from,
1619 to));
1620 parts_[i].tag = REPLACEMENT_SUBSTRING;
1621 parts_[i].data = substring_index;
1622 substring_index++;
1623 } else if (tag == REPLACEMENT_STRING) {
1624 replacement_substrings_.Add(replacement);
1625 parts_[i].data = substring_index;
1626 substring_index++;
1627 }
1628 }
1629}
1630
1631
1632void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1633 int match_from,
1634 int match_to,
1635 Handle<JSArray> last_match_info) {
1636 for (int i = 0, n = parts_.length(); i < n; i++) {
1637 ReplacementPart part = parts_[i];
1638 switch (part.tag) {
1639 case SUBJECT_PREFIX:
1640 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1641 break;
1642 case SUBJECT_SUFFIX: {
1643 int subject_length = part.data;
1644 if (match_to < subject_length) {
1645 builder->AddSubjectSlice(match_to, subject_length);
1646 }
1647 break;
1648 }
1649 case SUBJECT_CAPTURE: {
1650 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001651 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001652 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1653 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1654 if (from >= 0 && to > from) {
1655 builder->AddSubjectSlice(from, to);
1656 }
1657 break;
1658 }
1659 case REPLACEMENT_SUBSTRING:
1660 case REPLACEMENT_STRING:
1661 builder->AddString(replacement_substrings_[part.data]);
1662 break;
1663 default:
1664 UNREACHABLE();
1665 }
1666 }
1667}
1668
1669
1670
1671static Object* StringReplaceRegExpWithString(String* subject,
1672 JSRegExp* regexp,
1673 String* replacement,
1674 JSArray* last_match_info) {
1675 ASSERT(subject->IsFlat());
1676 ASSERT(replacement->IsFlat());
1677
1678 HandleScope handles;
1679
1680 int length = subject->length();
1681 Handle<String> subject_handle(subject);
1682 Handle<JSRegExp> regexp_handle(regexp);
1683 Handle<String> replacement_handle(replacement);
1684 Handle<JSArray> last_match_info_handle(last_match_info);
1685 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1686 subject_handle,
1687 0,
1688 last_match_info_handle);
1689 if (match.is_null()) {
1690 return Failure::Exception();
1691 }
1692 if (match->IsNull()) {
1693 return *subject_handle;
1694 }
1695
1696 int capture_count = regexp_handle->CaptureCount();
1697
1698 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001699 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001700 CompiledReplacement compiled_replacement;
1701 compiled_replacement.Compile(replacement_handle,
1702 capture_count,
1703 length);
1704
1705 bool is_global = regexp_handle->GetFlags().is_global();
1706
1707 // Guessing the number of parts that the final result string is built
1708 // from. Global regexps can match any number of times, so we guess
1709 // conservatively.
1710 int expected_parts =
1711 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1712 ReplacementStringBuilder builder(subject_handle, expected_parts);
1713
1714 // Index of end of last match.
1715 int prev = 0;
1716
1717 // Number of parts added by compiled replacement plus preceeding string
1718 // and possibly suffix after last match.
1719 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1720 bool matched = true;
1721 do {
1722 ASSERT(last_match_info_handle->HasFastElements());
1723 // Increase the capacity of the builder before entering local handle-scope,
1724 // so its internal buffer can safely allocate a new handle if it grows.
1725 builder.EnsureCapacity(parts_added_per_loop);
1726
1727 HandleScope loop_scope;
1728 int start, end;
1729 {
1730 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001731 FixedArray* match_info_array =
1732 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001733
1734 ASSERT_EQ(capture_count * 2 + 2,
1735 RegExpImpl::GetLastCaptureCount(match_info_array));
1736 start = RegExpImpl::GetCapture(match_info_array, 0);
1737 end = RegExpImpl::GetCapture(match_info_array, 1);
1738 }
1739
1740 if (prev < start) {
1741 builder.AddSubjectSlice(prev, start);
1742 }
1743 compiled_replacement.Apply(&builder,
1744 start,
1745 end,
1746 last_match_info_handle);
1747 prev = end;
1748
1749 // Only continue checking for global regexps.
1750 if (!is_global) break;
1751
1752 // Continue from where the match ended, unless it was an empty match.
1753 int next = end;
1754 if (start == end) {
1755 next = end + 1;
1756 if (next > length) break;
1757 }
1758
1759 match = RegExpImpl::Exec(regexp_handle,
1760 subject_handle,
1761 next,
1762 last_match_info_handle);
1763 if (match.is_null()) {
1764 return Failure::Exception();
1765 }
1766 matched = !match->IsNull();
1767 } while (matched);
1768
1769 if (prev < length) {
1770 builder.AddSubjectSlice(prev, length);
1771 }
1772
1773 return *(builder.ToString());
1774}
1775
1776
1777static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1778 ASSERT(args.length() == 4);
1779
1780 CONVERT_CHECKED(String, subject, args[0]);
1781 if (!subject->IsFlat()) {
1782 Object* flat_subject = subject->TryFlatten();
1783 if (flat_subject->IsFailure()) {
1784 return flat_subject;
1785 }
1786 subject = String::cast(flat_subject);
1787 }
1788
1789 CONVERT_CHECKED(String, replacement, args[2]);
1790 if (!replacement->IsFlat()) {
1791 Object* flat_replacement = replacement->TryFlatten();
1792 if (flat_replacement->IsFailure()) {
1793 return flat_replacement;
1794 }
1795 replacement = String::cast(flat_replacement);
1796 }
1797
1798 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1799 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1800
1801 ASSERT(last_match_info->HasFastElements());
1802
1803 return StringReplaceRegExpWithString(subject,
1804 regexp,
1805 replacement,
1806 last_match_info);
1807}
1808
1809
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001810
ager@chromium.org7c537e22008-10-16 08:43:32 +00001811// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1812// limit, we can fix the size of tables.
1813static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001814// Reduce alphabet to this size.
1815static const int kBMAlphabetSize = 0x100;
1816// For patterns below this length, the skip length of Boyer-Moore is too short
1817// to compensate for the algorithmic overhead compared to simple brute force.
1818static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001819
ager@chromium.org7c537e22008-10-16 08:43:32 +00001820// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1821// shift. Only allows the last kBMMaxShift characters of the needle
1822// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001823class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001824 public:
1825 BMGoodSuffixBuffers() {}
1826 inline void init(int needle_length) {
1827 ASSERT(needle_length > 1);
1828 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1829 int len = needle_length - start;
1830 biased_suffixes_ = suffixes_ - start;
1831 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1832 for (int i = 0; i <= len; i++) {
1833 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001834 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001835 }
1836 inline int& suffix(int index) {
1837 ASSERT(biased_suffixes_ + index >= suffixes_);
1838 return biased_suffixes_[index];
1839 }
1840 inline int& shift(int index) {
1841 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1842 return biased_good_suffix_shift_[index];
1843 }
1844 private:
1845 int suffixes_[kBMMaxShift + 1];
1846 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001847 int* biased_suffixes_;
1848 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001849 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1850};
1851
1852// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001853static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001854static BMGoodSuffixBuffers bmgs_buffers;
1855
1856// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001857template <typename pchar>
1858static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1859 int start) {
1860 // Run forwards to populate bad_char_table, so that *last* instance
1861 // of character equivalence class is the one registered.
1862 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001863 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1864 : kBMAlphabetSize;
1865 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001866 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001867 } else {
1868 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001869 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001870 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001871 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001872 for (int i = start; i < pattern.length() - 1; i++) {
1873 pchar c = pattern[i];
1874 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001875 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001876 }
1877}
1878
1879template <typename pchar>
1880static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001881 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001882 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001883 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001884 // Compute Good Suffix tables.
1885 bmgs_buffers.init(m);
1886
1887 bmgs_buffers.shift(m-1) = 1;
1888 bmgs_buffers.suffix(m) = m + 1;
1889 pchar last_char = pattern[m - 1];
1890 int suffix = m + 1;
1891 for (int i = m; i > start;) {
1892 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1893 if (bmgs_buffers.shift(suffix) == len) {
1894 bmgs_buffers.shift(suffix) = suffix - i;
1895 }
1896 suffix = bmgs_buffers.suffix(suffix);
1897 }
1898 i--;
1899 suffix--;
1900 bmgs_buffers.suffix(i) = suffix;
1901 if (suffix == m) {
1902 // No suffix to extend, so we check against last_char only.
1903 while (i > start && pattern[i - 1] != last_char) {
1904 if (bmgs_buffers.shift(m) == len) {
1905 bmgs_buffers.shift(m) = m - i;
1906 }
1907 i--;
1908 bmgs_buffers.suffix(i) = m;
1909 }
1910 if (i > start) {
1911 i--;
1912 suffix--;
1913 bmgs_buffers.suffix(i) = suffix;
1914 }
1915 }
1916 }
1917 if (suffix < m) {
1918 for (int i = start; i <= m; i++) {
1919 if (bmgs_buffers.shift(i) == len) {
1920 bmgs_buffers.shift(i) = suffix - start;
1921 }
1922 if (i == suffix) {
1923 suffix = bmgs_buffers.suffix(suffix);
1924 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001925 }
1926 }
1927}
1928
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001929template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001930static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001931 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001932 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001933 }
1934 if (sizeof(pchar) == 1) {
1935 if (char_code > String::kMaxAsciiCharCode) {
1936 return -1;
1937 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001938 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001939 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001940 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001941}
1942
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001943// Restricted simplified Boyer-Moore string matching.
1944// Uses only the bad-shift table of Boyer-Moore and only uses it
1945// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001946template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001947static int BoyerMooreHorspool(Vector<const schar> subject,
1948 Vector<const pchar> pattern,
1949 int start_index,
1950 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001951 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001952 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001953 // Only preprocess at most kBMMaxShift last characters of pattern.
1954 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001955
ager@chromium.org7c537e22008-10-16 08:43:32 +00001956 BoyerMoorePopulateBadCharTable(pattern, start);
1957
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001958 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001959 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001960 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001961 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001962 // Perform search
1963 for (idx = start_index; idx <= n - m;) {
1964 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001965 int c;
1966 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001968 int shift = j - bc_occ;
1969 idx += shift;
1970 badness += 1 - shift; // at most zero, so badness cannot increase.
1971 if (idx > n - m) {
1972 *complete = true;
1973 return -1;
1974 }
1975 }
1976 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001977 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001978 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001979 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001980 return idx;
1981 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001982 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001983 // Badness increases by the number of characters we have
1984 // checked, and decreases by the number of characters we
1985 // can skip by shifting. It's a measure of how we are doing
1986 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001987 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001988 if (badness > 0) {
1989 *complete = false;
1990 return idx;
1991 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001992 }
1993 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001994 *complete = true;
1995 return -1;
1996}
ager@chromium.org7c537e22008-10-16 08:43:32 +00001997
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001998
1999template <typename schar, typename pchar>
2000static int BoyerMooreIndexOf(Vector<const schar> subject,
2001 Vector<const pchar> pattern,
2002 int idx) {
2003 int n = subject.length();
2004 int m = pattern.length();
2005 // Only preprocess at most kBMMaxShift last characters of pattern.
2006 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2007
2008 // Build the Good Suffix table and continue searching.
2009 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2010 pchar last_char = pattern[m - 1];
2011 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002012 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002013 int j = m - 1;
2014 schar c;
2015 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002016 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002017 idx += shift;
2018 if (idx > n - m) {
2019 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002020 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002021 }
2022 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2023 if (j < 0) {
2024 return idx;
2025 } else if (j < start) {
2026 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002027 // Fall back on BMH shift.
2028 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002029 } else {
2030 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002031 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002032 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002033 if (gs_shift > shift) {
2034 shift = gs_shift;
2035 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002036 idx += shift;
2037 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002038 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002039
2040 return -1;
2041}
2042
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002043
2044template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002045static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002046 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002047 int start_index) {
2048 for (int i = start_index, n = string.length(); i < n; i++) {
2049 if (pattern_char == string[i]) {
2050 return i;
2051 }
2052 }
2053 return -1;
2054}
2055
2056// Trivial string search for shorter strings.
2057// On return, if "complete" is set to true, the return value is the
2058// final result of searching for the patter in the subject.
2059// If "complete" is set to false, the return value is the index where
2060// further checking should start, i.e., it's guaranteed that the pattern
2061// does not occur at a position prior to the returned index.
2062template <typename pchar, typename schar>
2063static int SimpleIndexOf(Vector<const schar> subject,
2064 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002065 int idx,
2066 bool* complete) {
2067 // Badness is a count of how much work we have done. When we have
2068 // done enough work we decide it's probably worth switching to a better
2069 // algorithm.
2070 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002071 // We know our pattern is at least 2 characters, we cache the first so
2072 // the common case of the first character not matching is faster.
2073 pchar pattern_first_char = pattern[0];
2074
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002075 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2076 badness++;
2077 if (badness > 0) {
2078 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002079 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002080 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002081 if (subject[i] != pattern_first_char) continue;
2082 int j = 1;
2083 do {
2084 if (pattern[j] != subject[i+j]) {
2085 break;
2086 }
2087 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002088 } while (j < pattern.length());
2089 if (j == pattern.length()) {
2090 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002091 return i;
2092 }
2093 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002094 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002095 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002096 return -1;
2097}
2098
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002099// Simple indexOf that never bails out. For short patterns only.
2100template <typename pchar, typename schar>
2101static int SimpleIndexOf(Vector<const schar> subject,
2102 Vector<const pchar> pattern,
2103 int idx) {
2104 pchar pattern_first_char = pattern[0];
2105 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2106 if (subject[i] != pattern_first_char) continue;
2107 int j = 1;
2108 do {
2109 if (pattern[j] != subject[i+j]) {
2110 break;
2111 }
2112 j++;
2113 } while (j < pattern.length());
2114 if (j == pattern.length()) {
2115 return i;
2116 }
2117 }
2118 return -1;
2119}
2120
2121
2122// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002123template <typename schar, typename pchar>
2124static int StringMatchStrategy(Vector<const schar> sub,
2125 Vector<const pchar> pat,
2126 int start_index) {
2127 ASSERT(pat.length() > 1);
2128
2129 // We have an ASCII haystack and a non-ASCII needle. Check if there
2130 // really is a non-ASCII character in the needle and bail out if there
2131 // is.
2132 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2133 for (int i = 0; i < pat.length(); i++) {
2134 uc16 c = pat[i];
2135 if (c > String::kMaxAsciiCharCode) {
2136 return -1;
2137 }
2138 }
2139 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002140 if (pat.length() < kBMMinPatternLength) {
2141 // We don't believe fancy searching can ever be more efficient.
2142 // The max shift of Boyer-Moore on a pattern of this length does
2143 // not compensate for the overhead.
2144 return SimpleIndexOf(sub, pat, start_index);
2145 }
2146 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002147 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002148 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2149 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002150 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002151 if (complete) return idx;
2152 return BoyerMooreIndexOf(sub, pat, idx);
2153}
2154
2155// Perform string match of pattern on subject, starting at start index.
2156// Caller must ensure that 0 <= start_index <= sub->length(),
2157// and should check that pat->length() + start_index <= sub->length()
2158int Runtime::StringMatch(Handle<String> sub,
2159 Handle<String> pat,
2160 int start_index) {
2161 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002162 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002163
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002164 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002165 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002166
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002167 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002168 if (start_index + pattern_length > subject_length) return -1;
2169
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002170 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002171 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002172 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002173 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002174 // character patterns linear search is necessary, so any smart
2175 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002176 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002177 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002178 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002179 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002180 if (pchar > String::kMaxAsciiCharCode) {
2181 return -1;
2182 }
2183 Vector<const char> ascii_vector =
2184 sub->ToAsciiVector().SubVector(start_index, subject_length);
2185 const void* pos = memchr(ascii_vector.start(),
2186 static_cast<const char>(pchar),
2187 static_cast<size_t>(ascii_vector.length()));
2188 if (pos == NULL) {
2189 return -1;
2190 }
2191 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2192 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002193 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002194 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002195 }
2196
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002197 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002198 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002199 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002200
ager@chromium.org7c537e22008-10-16 08:43:32 +00002201 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2202 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002203 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002204 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002205 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002206 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002207 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002208 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002209 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002210 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002211 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002212 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002213 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002214 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002215}
2216
2217
2218static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002219 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002220 ASSERT(args.length() == 3);
2221
ager@chromium.org7c537e22008-10-16 08:43:32 +00002222 CONVERT_ARG_CHECKED(String, sub, 0);
2223 CONVERT_ARG_CHECKED(String, pat, 1);
2224
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002225 Object* index = args[2];
2226 uint32_t start_index;
2227 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2228
ager@chromium.org870a0b62008-11-04 11:43:05 +00002229 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002230 int position = Runtime::StringMatch(sub, pat, start_index);
2231 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002232}
2233
2234
2235static Object* Runtime_StringLastIndexOf(Arguments args) {
2236 NoHandleAllocation ha;
2237 ASSERT(args.length() == 3);
2238
2239 CONVERT_CHECKED(String, sub, args[0]);
2240 CONVERT_CHECKED(String, pat, args[1]);
2241 Object* index = args[2];
2242
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002243 sub->TryFlattenIfNotFlat();
2244 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002245
2246 uint32_t start_index;
2247 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2248
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002249 uint32_t pattern_length = pat->length();
2250 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002251
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002252 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002253 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002254 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002255
2256 for (int i = start_index; i >= 0; i--) {
2257 bool found = true;
2258 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002259 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002260 found = false;
2261 break;
2262 }
2263 }
2264 if (found) return Smi::FromInt(i);
2265 }
2266
2267 return Smi::FromInt(-1);
2268}
2269
2270
2271static Object* Runtime_StringLocaleCompare(Arguments args) {
2272 NoHandleAllocation ha;
2273 ASSERT(args.length() == 2);
2274
2275 CONVERT_CHECKED(String, str1, args[0]);
2276 CONVERT_CHECKED(String, str2, args[1]);
2277
2278 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002279 int str1_length = str1->length();
2280 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002281
2282 // Decide trivial cases without flattening.
2283 if (str1_length == 0) {
2284 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2285 return Smi::FromInt(-str2_length);
2286 } else {
2287 if (str2_length == 0) return Smi::FromInt(str1_length);
2288 }
2289
2290 int end = str1_length < str2_length ? str1_length : str2_length;
2291
2292 // No need to flatten if we are going to find the answer on the first
2293 // character. At this point we know there is at least one character
2294 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002295 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002296 if (d != 0) return Smi::FromInt(d);
2297
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002298 str1->TryFlattenIfNotFlat();
2299 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002300
2301 static StringInputBuffer buf1;
2302 static StringInputBuffer buf2;
2303
2304 buf1.Reset(str1);
2305 buf2.Reset(str2);
2306
2307 for (int i = 0; i < end; i++) {
2308 uint16_t char1 = buf1.GetNext();
2309 uint16_t char2 = buf2.GetNext();
2310 if (char1 != char2) return Smi::FromInt(char1 - char2);
2311 }
2312
2313 return Smi::FromInt(str1_length - str2_length);
2314}
2315
2316
2317static Object* Runtime_StringSlice(Arguments args) {
2318 NoHandleAllocation ha;
2319 ASSERT(args.length() == 3);
2320
2321 CONVERT_CHECKED(String, value, args[0]);
2322 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2323 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2324
2325 int start = FastD2I(from_number);
2326 int end = FastD2I(to_number);
2327
2328 RUNTIME_ASSERT(end >= start);
2329 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002330 RUNTIME_ASSERT(end <= value->length());
2331 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002332}
2333
2334
ager@chromium.org41826e72009-03-30 13:30:57 +00002335static Object* Runtime_StringMatch(Arguments args) {
2336 ASSERT_EQ(3, args.length());
2337
2338 CONVERT_ARG_CHECKED(String, subject, 0);
2339 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2340 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2341 HandleScope handles;
2342
2343 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2344
2345 if (match.is_null()) {
2346 return Failure::Exception();
2347 }
2348 if (match->IsNull()) {
2349 return Heap::null_value();
2350 }
2351 int length = subject->length();
2352
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002353 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002354 ZoneList<int> offsets(8);
2355 do {
2356 int start;
2357 int end;
2358 {
2359 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002360 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002361 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2362 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2363 }
2364 offsets.Add(start);
2365 offsets.Add(end);
2366 int index = start < end ? end : end + 1;
2367 if (index > length) break;
2368 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2369 if (match.is_null()) {
2370 return Failure::Exception();
2371 }
2372 } while (!match->IsNull());
2373 int matches = offsets.length() / 2;
2374 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2375 for (int i = 0; i < matches ; i++) {
2376 int from = offsets.at(i * 2);
2377 int to = offsets.at(i * 2 + 1);
2378 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2379 }
2380 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2381 result->set_length(Smi::FromInt(matches));
2382 return *result;
2383}
2384
2385
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002386static Object* Runtime_NumberToRadixString(Arguments args) {
2387 NoHandleAllocation ha;
2388 ASSERT(args.length() == 2);
2389
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002390 // Fast case where the result is a one character string.
2391 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2392 int value = Smi::cast(args[0])->value();
2393 int radix = Smi::cast(args[1])->value();
2394 if (value >= 0 && value < radix) {
2395 RUNTIME_ASSERT(radix <= 36);
2396 // Character array used for conversion.
2397 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2398 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2399 }
2400 }
2401
2402 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002403 CONVERT_DOUBLE_CHECKED(value, args[0]);
2404 if (isnan(value)) {
2405 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2406 }
2407 if (isinf(value)) {
2408 if (value < 0) {
2409 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2410 }
2411 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2412 }
2413 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2414 int radix = FastD2I(radix_number);
2415 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2416 char* str = DoubleToRadixCString(value, radix);
2417 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2418 DeleteArray(str);
2419 return result;
2420}
2421
2422
2423static Object* Runtime_NumberToFixed(Arguments args) {
2424 NoHandleAllocation ha;
2425 ASSERT(args.length() == 2);
2426
2427 CONVERT_DOUBLE_CHECKED(value, args[0]);
2428 if (isnan(value)) {
2429 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2430 }
2431 if (isinf(value)) {
2432 if (value < 0) {
2433 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2434 }
2435 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2436 }
2437 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2438 int f = FastD2I(f_number);
2439 RUNTIME_ASSERT(f >= 0);
2440 char* str = DoubleToFixedCString(value, f);
2441 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2442 DeleteArray(str);
2443 return res;
2444}
2445
2446
2447static Object* Runtime_NumberToExponential(Arguments args) {
2448 NoHandleAllocation ha;
2449 ASSERT(args.length() == 2);
2450
2451 CONVERT_DOUBLE_CHECKED(value, args[0]);
2452 if (isnan(value)) {
2453 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2454 }
2455 if (isinf(value)) {
2456 if (value < 0) {
2457 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2458 }
2459 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2460 }
2461 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2462 int f = FastD2I(f_number);
2463 RUNTIME_ASSERT(f >= -1 && f <= 20);
2464 char* str = DoubleToExponentialCString(value, f);
2465 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2466 DeleteArray(str);
2467 return res;
2468}
2469
2470
2471static Object* Runtime_NumberToPrecision(Arguments args) {
2472 NoHandleAllocation ha;
2473 ASSERT(args.length() == 2);
2474
2475 CONVERT_DOUBLE_CHECKED(value, args[0]);
2476 if (isnan(value)) {
2477 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2478 }
2479 if (isinf(value)) {
2480 if (value < 0) {
2481 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2482 }
2483 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2484 }
2485 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2486 int f = FastD2I(f_number);
2487 RUNTIME_ASSERT(f >= 1 && f <= 21);
2488 char* str = DoubleToPrecisionCString(value, f);
2489 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2490 DeleteArray(str);
2491 return res;
2492}
2493
2494
2495// Returns a single character string where first character equals
2496// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002497static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002498 if (index < static_cast<uint32_t>(string->length())) {
2499 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002500 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002501 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002502 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002503 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002504}
2505
2506
2507Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2508 // Handle [] indexing on Strings
2509 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002510 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2511 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002512 }
2513
2514 // Handle [] indexing on String objects
2515 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002516 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2517 Handle<Object> result =
2518 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2519 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002520 }
2521
2522 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002523 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524 return prototype->GetElement(index);
2525 }
2526
2527 return object->GetElement(index);
2528}
2529
2530
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002531Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2532 HandleScope scope;
2533
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002534 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002535 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002536 Handle<Object> error =
2537 Factory::NewTypeError("non_object_property_load",
2538 HandleVector(args, 2));
2539 return Top::Throw(*error);
2540 }
2541
2542 // Check if the given key is an array index.
2543 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002544 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002545 return GetElementOrCharAt(object, index);
2546 }
2547
2548 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002549 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002550 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002551 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002552 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002553 bool has_pending_exception = false;
2554 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002555 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002556 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002557 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002558 }
2559
ager@chromium.org32912102009-01-16 10:38:43 +00002560 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561 // the element if so.
2562 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002563 return GetElementOrCharAt(object, index);
2564 } else {
2565 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002566 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002567 }
2568}
2569
2570
2571static Object* Runtime_GetProperty(Arguments args) {
2572 NoHandleAllocation ha;
2573 ASSERT(args.length() == 2);
2574
2575 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002576 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002577
2578 return Runtime::GetObjectProperty(object, key);
2579}
2580
2581
ager@chromium.org7c537e22008-10-16 08:43:32 +00002582
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002583// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002584static Object* Runtime_KeyedGetProperty(Arguments args) {
2585 NoHandleAllocation ha;
2586 ASSERT(args.length() == 2);
2587
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002588 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002589 // itself.
2590 //
2591 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002592 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002593 // global proxy object never has properties. This is the case
2594 // because the global proxy object forwards everything to its hidden
2595 // prototype including local lookups.
2596 //
2597 // Additionally, we need to make sure that we do not cache results
2598 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002599 if (args[0]->IsJSObject() &&
2600 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002601 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002602 args[1]->IsString()) {
2603 JSObject* receiver = JSObject::cast(args[0]);
2604 String* key = String::cast(args[1]);
2605 if (receiver->HasFastProperties()) {
2606 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002607 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002608 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2609 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002610 Object* value = receiver->FastPropertyAt(offset);
2611 return value->IsTheHole() ? Heap::undefined_value() : value;
2612 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002613 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002614 LookupResult result;
2615 receiver->LocalLookup(key, &result);
2616 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2617 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002618 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002619 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620 }
2621 } else {
2622 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002623 StringDictionary* dictionary = receiver->property_dictionary();
2624 int entry = dictionary->FindEntry(key);
2625 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002626 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002627 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002628 if (!receiver->IsGlobalObject()) return value;
2629 value = JSGlobalPropertyCell::cast(value)->value();
2630 if (!value->IsTheHole()) return value;
2631 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002632 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002633 }
2634 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002635
2636 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002637 return Runtime::GetObjectProperty(args.at<Object>(0),
2638 args.at<Object>(1));
2639}
2640
2641
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002642Object* Runtime::SetObjectProperty(Handle<Object> object,
2643 Handle<Object> key,
2644 Handle<Object> value,
2645 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002646 HandleScope scope;
2647
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002648 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002649 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002650 Handle<Object> error =
2651 Factory::NewTypeError("non_object_property_store",
2652 HandleVector(args, 2));
2653 return Top::Throw(*error);
2654 }
2655
2656 // If the object isn't a JavaScript object, we ignore the store.
2657 if (!object->IsJSObject()) return *value;
2658
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002659 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2660
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661 // Check if the given key is an array index.
2662 uint32_t index;
2663 if (Array::IndexFromObject(*key, &index)) {
2664 ASSERT(attr == NONE);
2665
2666 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2667 // of a string using [] notation. We need to support this too in
2668 // JavaScript.
2669 // In the case of a String object we just need to redirect the assignment to
2670 // the underlying string if the index is in range. Since the underlying
2671 // string does nothing with the assignment then we can ignore such
2672 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002673 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002674 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002675 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002676
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002677 Handle<Object> result = SetElement(js_object, index, value);
2678 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002679 return *value;
2680 }
2681
2682 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002683 Handle<Object> result;
2684 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002686 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002687 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002688 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002689 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002690 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002691 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002692 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002693 return *value;
2694 }
2695
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002696 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002697 bool has_pending_exception = false;
2698 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2699 if (has_pending_exception) return Failure::Exception();
2700 Handle<String> name = Handle<String>::cast(converted);
2701
2702 if (name->AsArrayIndex(&index)) {
2703 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002704 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002705 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002706 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002707 }
2708}
2709
2710
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002711Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2712 Handle<Object> key,
2713 Handle<Object> value,
2714 PropertyAttributes attr) {
2715 HandleScope scope;
2716
2717 // Check if the given key is an array index.
2718 uint32_t index;
2719 if (Array::IndexFromObject(*key, &index)) {
2720 ASSERT(attr == NONE);
2721
2722 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2723 // of a string using [] notation. We need to support this too in
2724 // JavaScript.
2725 // In the case of a String object we just need to redirect the assignment to
2726 // the underlying string if the index is in range. Since the underlying
2727 // string does nothing with the assignment then we can ignore such
2728 // assignments.
2729 if (js_object->IsStringObjectWithCharacterAt(index)) {
2730 return *value;
2731 }
2732
2733 return js_object->SetElement(index, *value);
2734 }
2735
2736 if (key->IsString()) {
2737 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2738 ASSERT(attr == NONE);
2739 return js_object->SetElement(index, *value);
2740 } else {
2741 Handle<String> key_string = Handle<String>::cast(key);
2742 key_string->TryFlattenIfNotFlat();
2743 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2744 *value,
2745 attr);
2746 }
2747 }
2748
2749 // Call-back into JavaScript to convert the key to a string.
2750 bool has_pending_exception = false;
2751 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2752 if (has_pending_exception) return Failure::Exception();
2753 Handle<String> name = Handle<String>::cast(converted);
2754
2755 if (name->AsArrayIndex(&index)) {
2756 ASSERT(attr == NONE);
2757 return js_object->SetElement(index, *value);
2758 } else {
2759 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2760 }
2761}
2762
2763
ager@chromium.orge2902be2009-06-08 12:21:35 +00002764Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2765 Handle<Object> key) {
2766 HandleScope scope;
2767
2768 // Check if the given key is an array index.
2769 uint32_t index;
2770 if (Array::IndexFromObject(*key, &index)) {
2771 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2772 // characters of a string using [] notation. In the case of a
2773 // String object we just need to redirect the deletion to the
2774 // underlying string if the index is in range. Since the
2775 // underlying string does nothing with the deletion, we can ignore
2776 // such deletions.
2777 if (js_object->IsStringObjectWithCharacterAt(index)) {
2778 return Heap::true_value();
2779 }
2780
2781 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2782 }
2783
2784 Handle<String> key_string;
2785 if (key->IsString()) {
2786 key_string = Handle<String>::cast(key);
2787 } else {
2788 // Call-back into JavaScript to convert the key to a string.
2789 bool has_pending_exception = false;
2790 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2791 if (has_pending_exception) return Failure::Exception();
2792 key_string = Handle<String>::cast(converted);
2793 }
2794
2795 key_string->TryFlattenIfNotFlat();
2796 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2797}
2798
2799
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002800static Object* Runtime_SetProperty(Arguments args) {
2801 NoHandleAllocation ha;
2802 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2803
2804 Handle<Object> object = args.at<Object>(0);
2805 Handle<Object> key = args.at<Object>(1);
2806 Handle<Object> value = args.at<Object>(2);
2807
2808 // Compute attributes.
2809 PropertyAttributes attributes = NONE;
2810 if (args.length() == 4) {
2811 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002812 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002813 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002814 RUNTIME_ASSERT(
2815 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2816 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002817 }
2818 return Runtime::SetObjectProperty(object, key, value, attributes);
2819}
2820
2821
2822// Set a local property, even if it is READ_ONLY. If the property does not
2823// exist, it will be added with attributes NONE.
2824static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2825 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002826 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002827 CONVERT_CHECKED(JSObject, object, args[0]);
2828 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002829 // Compute attributes.
2830 PropertyAttributes attributes = NONE;
2831 if (args.length() == 4) {
2832 CONVERT_CHECKED(Smi, value_obj, args[3]);
2833 int unchecked_value = value_obj->value();
2834 // Only attribute bits should be set.
2835 RUNTIME_ASSERT(
2836 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2837 attributes = static_cast<PropertyAttributes>(unchecked_value);
2838 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002839
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002840 return object->
2841 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002842}
2843
2844
2845static Object* Runtime_DeleteProperty(Arguments args) {
2846 NoHandleAllocation ha;
2847 ASSERT(args.length() == 2);
2848
2849 CONVERT_CHECKED(JSObject, object, args[0]);
2850 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002851 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002852}
2853
2854
ager@chromium.org9085a012009-05-11 19:22:57 +00002855static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2856 Handle<String> key) {
2857 if (object->HasLocalProperty(*key)) return Heap::true_value();
2858 // Handle hidden prototypes. If there's a hidden prototype above this thing
2859 // then we have to check it for properties, because they are supposed to
2860 // look like they are on this object.
2861 Handle<Object> proto(object->GetPrototype());
2862 if (proto->IsJSObject() &&
2863 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2864 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2865 }
2866 return Heap::false_value();
2867}
2868
2869
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002870static Object* Runtime_HasLocalProperty(Arguments args) {
2871 NoHandleAllocation ha;
2872 ASSERT(args.length() == 2);
2873 CONVERT_CHECKED(String, key, args[1]);
2874
ager@chromium.org9085a012009-05-11 19:22:57 +00002875 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002876 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002877 if (obj->IsJSObject()) {
2878 JSObject* object = JSObject::cast(obj);
2879 // Fast case - no interceptors.
2880 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2881 // Slow case. Either it's not there or we have an interceptor. We should
2882 // have handles for this kind of deal.
2883 HandleScope scope;
2884 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2885 Handle<String>(key));
2886 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887 // Well, there is one exception: Handle [] on strings.
2888 uint32_t index;
2889 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002890 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002891 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002892 return Heap::true_value();
2893 }
2894 }
2895 return Heap::false_value();
2896}
2897
2898
2899static Object* Runtime_HasProperty(Arguments args) {
2900 NoHandleAllocation na;
2901 ASSERT(args.length() == 2);
2902
2903 // Only JS objects can have properties.
2904 if (args[0]->IsJSObject()) {
2905 JSObject* object = JSObject::cast(args[0]);
2906 CONVERT_CHECKED(String, key, args[1]);
2907 if (object->HasProperty(key)) return Heap::true_value();
2908 }
2909 return Heap::false_value();
2910}
2911
2912
2913static Object* Runtime_HasElement(Arguments args) {
2914 NoHandleAllocation na;
2915 ASSERT(args.length() == 2);
2916
2917 // Only JS objects can have elements.
2918 if (args[0]->IsJSObject()) {
2919 JSObject* object = JSObject::cast(args[0]);
2920 CONVERT_CHECKED(Smi, index_obj, args[1]);
2921 uint32_t index = index_obj->value();
2922 if (object->HasElement(index)) return Heap::true_value();
2923 }
2924 return Heap::false_value();
2925}
2926
2927
2928static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2929 NoHandleAllocation ha;
2930 ASSERT(args.length() == 2);
2931
2932 CONVERT_CHECKED(JSObject, object, args[0]);
2933 CONVERT_CHECKED(String, key, args[1]);
2934
2935 uint32_t index;
2936 if (key->AsArrayIndex(&index)) {
2937 return Heap::ToBoolean(object->HasElement(index));
2938 }
2939
ager@chromium.org870a0b62008-11-04 11:43:05 +00002940 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2941 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002942}
2943
2944
2945static Object* Runtime_GetPropertyNames(Arguments args) {
2946 HandleScope scope;
2947 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002948 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002949 return *GetKeysFor(object);
2950}
2951
2952
2953// Returns either a FixedArray as Runtime_GetPropertyNames,
2954// or, if the given object has an enum cache that contains
2955// all enumerable properties of the object and its prototypes
2956// have none, the map of the object. This is used to speed up
2957// the check for deletions during a for-in.
2958static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2959 ASSERT(args.length() == 1);
2960
2961 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2962
2963 if (raw_object->IsSimpleEnum()) return raw_object->map();
2964
2965 HandleScope scope;
2966 Handle<JSObject> object(raw_object);
2967 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2968
2969 // Test again, since cache may have been built by preceding call.
2970 if (object->IsSimpleEnum()) return object->map();
2971
2972 return *content;
2973}
2974
2975
2976static Object* Runtime_GetArgumentsProperty(Arguments args) {
2977 NoHandleAllocation ha;
2978 ASSERT(args.length() == 1);
2979
2980 // Compute the frame holding the arguments.
2981 JavaScriptFrameIterator it;
2982 it.AdvanceToArgumentsFrame();
2983 JavaScriptFrame* frame = it.frame();
2984
2985 // Get the actual number of provided arguments.
2986 const uint32_t n = frame->GetProvidedParametersCount();
2987
2988 // Try to convert the key to an index. If successful and within
2989 // index return the the argument from the frame.
2990 uint32_t index;
2991 if (Array::IndexFromObject(args[0], &index) && index < n) {
2992 return frame->GetParameter(index);
2993 }
2994
2995 // Convert the key to a string.
2996 HandleScope scope;
2997 bool exception = false;
2998 Handle<Object> converted =
2999 Execution::ToString(args.at<Object>(0), &exception);
3000 if (exception) return Failure::Exception();
3001 Handle<String> key = Handle<String>::cast(converted);
3002
3003 // Try to convert the string key into an array index.
3004 if (key->AsArrayIndex(&index)) {
3005 if (index < n) {
3006 return frame->GetParameter(index);
3007 } else {
3008 return Top::initial_object_prototype()->GetElement(index);
3009 }
3010 }
3011
3012 // Handle special arguments properties.
3013 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3014 if (key->Equals(Heap::callee_symbol())) return frame->function();
3015
3016 // Lookup in the initial Object.prototype object.
3017 return Top::initial_object_prototype()->GetProperty(*key);
3018}
3019
3020
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003021static Object* Runtime_ToFastProperties(Arguments args) {
3022 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003023 Handle<Object> object = args.at<Object>(0);
3024 if (object->IsJSObject()) {
3025 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3026 js_object->TransformToFastProperties(0);
3027 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003028 return *object;
3029}
3030
3031
3032static Object* Runtime_ToSlowProperties(Arguments args) {
3033 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003034 Handle<Object> object = args.at<Object>(0);
3035 if (object->IsJSObject()) {
3036 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003037 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003038 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003039 return *object;
3040}
3041
3042
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003043static Object* Runtime_ToBool(Arguments args) {
3044 NoHandleAllocation ha;
3045 ASSERT(args.length() == 1);
3046
3047 return args[0]->ToBoolean();
3048}
3049
3050
3051// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3052// Possible optimizations: put the type string into the oddballs.
3053static Object* Runtime_Typeof(Arguments args) {
3054 NoHandleAllocation ha;
3055
3056 Object* obj = args[0];
3057 if (obj->IsNumber()) return Heap::number_symbol();
3058 HeapObject* heap_obj = HeapObject::cast(obj);
3059
3060 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003061 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003062
3063 InstanceType instance_type = heap_obj->map()->instance_type();
3064 if (instance_type < FIRST_NONSTRING_TYPE) {
3065 return Heap::string_symbol();
3066 }
3067
3068 switch (instance_type) {
3069 case ODDBALL_TYPE:
3070 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3071 return Heap::boolean_symbol();
3072 }
3073 if (heap_obj->IsNull()) {
3074 return Heap::object_symbol();
3075 }
3076 ASSERT(heap_obj->IsUndefined());
3077 return Heap::undefined_symbol();
3078 case JS_FUNCTION_TYPE:
3079 return Heap::function_symbol();
3080 default:
3081 // For any kind of object not handled above, the spec rule for
3082 // host objects gives that it is okay to return "object"
3083 return Heap::object_symbol();
3084 }
3085}
3086
3087
3088static Object* Runtime_StringToNumber(Arguments args) {
3089 NoHandleAllocation ha;
3090 ASSERT(args.length() == 1);
3091 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003092 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003093 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3094}
3095
3096
3097static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3098 NoHandleAllocation ha;
3099 ASSERT(args.length() == 1);
3100
3101 CONVERT_CHECKED(JSArray, codes, args[0]);
3102 int length = Smi::cast(codes->length())->value();
3103
3104 // Check if the string can be ASCII.
3105 int i;
3106 for (i = 0; i < length; i++) {
3107 Object* element = codes->GetElement(i);
3108 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3109 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3110 break;
3111 }
3112
3113 Object* object = NULL;
3114 if (i == length) { // The string is ASCII.
3115 object = Heap::AllocateRawAsciiString(length);
3116 } else { // The string is not ASCII.
3117 object = Heap::AllocateRawTwoByteString(length);
3118 }
3119
3120 if (object->IsFailure()) return object;
3121 String* result = String::cast(object);
3122 for (int i = 0; i < length; i++) {
3123 Object* element = codes->GetElement(i);
3124 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003125 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003126 }
3127 return result;
3128}
3129
3130
3131// kNotEscaped is generated by the following:
3132//
3133// #!/bin/perl
3134// for (my $i = 0; $i < 256; $i++) {
3135// print "\n" if $i % 16 == 0;
3136// my $c = chr($i);
3137// my $escaped = 1;
3138// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3139// print $escaped ? "0, " : "1, ";
3140// }
3141
3142
3143static bool IsNotEscaped(uint16_t character) {
3144 // Only for 8 bit characters, the rest are always escaped (in a different way)
3145 ASSERT(character < 256);
3146 static const char kNotEscaped[256] = {
3147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3150 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3151 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3152 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3153 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3154 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3163 };
3164 return kNotEscaped[character] != 0;
3165}
3166
3167
3168static Object* Runtime_URIEscape(Arguments args) {
3169 const char hex_chars[] = "0123456789ABCDEF";
3170 NoHandleAllocation ha;
3171 ASSERT(args.length() == 1);
3172 CONVERT_CHECKED(String, source, args[0]);
3173
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003174 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003175
3176 int escaped_length = 0;
3177 int length = source->length();
3178 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003179 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003180 buffer->Reset(source);
3181 while (buffer->has_more()) {
3182 uint16_t character = buffer->GetNext();
3183 if (character >= 256) {
3184 escaped_length += 6;
3185 } else if (IsNotEscaped(character)) {
3186 escaped_length++;
3187 } else {
3188 escaped_length += 3;
3189 }
3190 // We don't allow strings that are longer than Smi range.
3191 if (!Smi::IsValid(escaped_length)) {
3192 Top::context()->mark_out_of_memory();
3193 return Failure::OutOfMemoryException();
3194 }
3195 }
3196 }
3197 // No length change implies no change. Return original string if no change.
3198 if (escaped_length == length) {
3199 return source;
3200 }
3201 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3202 if (o->IsFailure()) return o;
3203 String* destination = String::cast(o);
3204 int dest_position = 0;
3205
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003206 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003207 buffer->Rewind();
3208 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003209 uint16_t chr = buffer->GetNext();
3210 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003211 destination->Set(dest_position, '%');
3212 destination->Set(dest_position+1, 'u');
3213 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3214 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3215 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3216 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003217 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003218 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003219 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003220 dest_position++;
3221 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003222 destination->Set(dest_position, '%');
3223 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3224 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003225 dest_position += 3;
3226 }
3227 }
3228 return destination;
3229}
3230
3231
3232static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3233 static const signed char kHexValue['g'] = {
3234 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3235 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3236 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3237 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3238 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3239 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3240 -1, 10, 11, 12, 13, 14, 15 };
3241
3242 if (character1 > 'f') return -1;
3243 int hi = kHexValue[character1];
3244 if (hi == -1) return -1;
3245 if (character2 > 'f') return -1;
3246 int lo = kHexValue[character2];
3247 if (lo == -1) return -1;
3248 return (hi << 4) + lo;
3249}
3250
3251
ager@chromium.org870a0b62008-11-04 11:43:05 +00003252static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003253 int i,
3254 int length,
3255 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003256 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003257 int32_t hi = 0;
3258 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003259 if (character == '%' &&
3260 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003261 source->Get(i + 1) == 'u' &&
3262 (hi = TwoDigitHex(source->Get(i + 2),
3263 source->Get(i + 3))) != -1 &&
3264 (lo = TwoDigitHex(source->Get(i + 4),
3265 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003266 *step = 6;
3267 return (hi << 8) + lo;
3268 } else if (character == '%' &&
3269 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003270 (lo = TwoDigitHex(source->Get(i + 1),
3271 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003272 *step = 3;
3273 return lo;
3274 } else {
3275 *step = 1;
3276 return character;
3277 }
3278}
3279
3280
3281static Object* Runtime_URIUnescape(Arguments args) {
3282 NoHandleAllocation ha;
3283 ASSERT(args.length() == 1);
3284 CONVERT_CHECKED(String, source, args[0]);
3285
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003286 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003287
3288 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003289 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003290
3291 int unescaped_length = 0;
3292 for (int i = 0; i < length; unescaped_length++) {
3293 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003294 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003295 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003296 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003297 i += step;
3298 }
3299
3300 // No length change implies no change. Return original string if no change.
3301 if (unescaped_length == length)
3302 return source;
3303
3304 Object* o = ascii ?
3305 Heap::AllocateRawAsciiString(unescaped_length) :
3306 Heap::AllocateRawTwoByteString(unescaped_length);
3307 if (o->IsFailure()) return o;
3308 String* destination = String::cast(o);
3309
3310 int dest_position = 0;
3311 for (int i = 0; i < length; dest_position++) {
3312 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003313 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003314 i += step;
3315 }
3316 return destination;
3317}
3318
3319
3320static Object* Runtime_StringParseInt(Arguments args) {
3321 NoHandleAllocation ha;
3322
3323 CONVERT_CHECKED(String, s, args[0]);
3324 CONVERT_DOUBLE_CHECKED(n, args[1]);
3325 int radix = FastD2I(n);
3326
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003327 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003328
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003329 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330 int i;
3331
3332 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003333 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003334 if (i == len) return Heap::nan_value();
3335
3336 // Compute the sign (default to +).
3337 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003338 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003339 sign = -1;
3340 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003341 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003342 i++;
3343 }
3344
3345 // Compute the radix if 0.
3346 if (radix == 0) {
3347 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003348 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003349 radix = 8;
3350 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003351 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003352 if (c == 'x' || c == 'X') {
3353 radix = 16;
3354 i += 2;
3355 }
3356 }
3357 }
3358 } else if (radix == 16) {
3359 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003360 if (i + 1 < len && s->Get(i) == '0') {
3361 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003362 if (c == 'x' || c == 'X') i += 2;
3363 }
3364 }
3365
3366 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3367 double value;
3368 int end_index = StringToInt(s, i, radix, &value);
3369 if (end_index != i) {
3370 return Heap::NumberFromDouble(sign * value);
3371 }
3372 return Heap::nan_value();
3373}
3374
3375
3376static Object* Runtime_StringParseFloat(Arguments args) {
3377 NoHandleAllocation ha;
3378 CONVERT_CHECKED(String, str, args[0]);
3379
3380 // ECMA-262 section 15.1.2.3, empty string is NaN
3381 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3382
3383 // Create a number object from the value.
3384 return Heap::NumberFromDouble(value);
3385}
3386
3387
3388static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3389static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3390
3391
3392template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003393static Object* ConvertCaseHelper(String* s,
3394 int length,
3395 int input_string_length,
3396 unibrow::Mapping<Converter, 128>* mapping) {
3397 // We try this twice, once with the assumption that the result is no longer
3398 // than the input and, if that assumption breaks, again with the exact
3399 // length. This may not be pretty, but it is nicer than what was here before
3400 // and I hereby claim my vaffel-is.
3401 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003402 // Allocate the resulting string.
3403 //
3404 // NOTE: This assumes that the upper/lower case of an ascii
3405 // character is also ascii. This is currently the case, but it
3406 // might break in the future if we implement more context and locale
3407 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003408 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003409 ? Heap::AllocateRawAsciiString(length)
3410 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003411 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003412 String* result = String::cast(o);
3413 bool has_changed_character = false;
3414
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003415 // Convert all characters to upper case, assuming that they will fit
3416 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003417 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003418 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003419 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003420 // We can assume that the string is not empty
3421 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003422 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003423 bool has_next = buffer->has_more();
3424 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003425 int char_length = mapping->get(current, next, chars);
3426 if (char_length == 0) {
3427 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003428 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003429 i++;
3430 } else if (char_length == 1) {
3431 // Common case: converting the letter resulted in one character.
3432 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003433 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003434 has_changed_character = true;
3435 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003436 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003437 // We've assumed that the result would be as long as the
3438 // input but here is a character that converts to several
3439 // characters. No matter, we calculate the exact length
3440 // of the result and try the whole thing again.
3441 //
3442 // Note that this leaves room for optimization. We could just
3443 // memcpy what we already have to the result string. Also,
3444 // the result string is the last object allocated we could
3445 // "realloc" it and probably, in the vast majority of cases,
3446 // extend the existing string to be able to hold the full
3447 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003448 int next_length = 0;
3449 if (has_next) {
3450 next_length = mapping->get(next, 0, chars);
3451 if (next_length == 0) next_length = 1;
3452 }
3453 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003454 while (buffer->has_more()) {
3455 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003456 // NOTE: we use 0 as the next character here because, while
3457 // the next character may affect what a character converts to,
3458 // it does not in any case affect the length of what it convert
3459 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003460 int char_length = mapping->get(current, 0, chars);
3461 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003462 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003463 if (current_length > Smi::kMaxValue) {
3464 Top::context()->mark_out_of_memory();
3465 return Failure::OutOfMemoryException();
3466 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003467 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003468 // Try again with the real length.
3469 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003470 } else {
3471 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003472 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003473 i++;
3474 }
3475 has_changed_character = true;
3476 }
3477 current = next;
3478 }
3479 if (has_changed_character) {
3480 return result;
3481 } else {
3482 // If we didn't actually change anything in doing the conversion
3483 // we simple return the result and let the converted string
3484 // become garbage; there is no reason to keep two identical strings
3485 // alive.
3486 return s;
3487 }
3488}
3489
3490
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003491template <class Converter>
3492static Object* ConvertCase(Arguments args,
3493 unibrow::Mapping<Converter, 128>* mapping) {
3494 NoHandleAllocation ha;
3495
3496 CONVERT_CHECKED(String, s, args[0]);
3497 s->TryFlattenIfNotFlat();
3498
3499 int input_string_length = s->length();
3500 // Assume that the string is not empty; we need this assumption later
3501 if (input_string_length == 0) return s;
3502 int length = input_string_length;
3503
3504 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3505 if (answer->IsSmi()) {
3506 // Retry with correct length.
3507 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3508 }
3509 return answer; // This may be a failure.
3510}
3511
3512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003513static Object* Runtime_StringToLowerCase(Arguments args) {
3514 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3515}
3516
3517
3518static Object* Runtime_StringToUpperCase(Arguments args) {
3519 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3520}
3521
3522
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003523bool Runtime::IsUpperCaseChar(uint16_t ch) {
3524 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3525 int char_length = to_upper_mapping.get(ch, 0, chars);
3526 return char_length == 0;
3527}
3528
3529
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003530static Object* Runtime_NumberToString(Arguments args) {
3531 NoHandleAllocation ha;
3532 ASSERT(args.length() == 1);
3533
3534 Object* number = args[0];
3535 RUNTIME_ASSERT(number->IsNumber());
3536
3537 Object* cached = Heap::GetNumberStringCache(number);
3538 if (cached != Heap::undefined_value()) {
3539 return cached;
3540 }
3541
3542 char arr[100];
3543 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3544 const char* str;
3545 if (number->IsSmi()) {
3546 int num = Smi::cast(number)->value();
3547 str = IntToCString(num, buffer);
3548 } else {
3549 double num = HeapNumber::cast(number)->value();
3550 str = DoubleToCString(num, buffer);
3551 }
3552 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3553
3554 if (!result->IsFailure()) {
3555 Heap::SetNumberStringCache(number, String::cast(result));
3556 }
3557 return result;
3558}
3559
3560
3561static Object* Runtime_NumberToInteger(Arguments args) {
3562 NoHandleAllocation ha;
3563 ASSERT(args.length() == 1);
3564
3565 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003566 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003567 CONVERT_DOUBLE_CHECKED(number, obj);
3568 return Heap::NumberFromDouble(DoubleToInteger(number));
3569}
3570
3571
3572static Object* Runtime_NumberToJSUint32(Arguments args) {
3573 NoHandleAllocation ha;
3574 ASSERT(args.length() == 1);
3575
3576 Object* obj = args[0];
3577 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3578 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3579 return Heap::NumberFromUint32(number);
3580}
3581
3582
3583static Object* Runtime_NumberToJSInt32(Arguments args) {
3584 NoHandleAllocation ha;
3585 ASSERT(args.length() == 1);
3586
3587 Object* obj = args[0];
3588 if (obj->IsSmi()) return obj;
3589 CONVERT_DOUBLE_CHECKED(number, obj);
3590 return Heap::NumberFromInt32(DoubleToInt32(number));
3591}
3592
3593
ager@chromium.org870a0b62008-11-04 11:43:05 +00003594// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3595// a small integer.
3596static Object* Runtime_NumberToSmi(Arguments args) {
3597 NoHandleAllocation ha;
3598 ASSERT(args.length() == 1);
3599
3600 Object* obj = args[0];
3601 if (obj->IsSmi()) {
3602 return obj;
3603 }
3604 if (obj->IsHeapNumber()) {
3605 double value = HeapNumber::cast(obj)->value();
3606 int int_value = FastD2I(value);
3607 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3608 return Smi::FromInt(int_value);
3609 }
3610 }
3611 return Heap::nan_value();
3612}
3613
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003614
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003615static Object* Runtime_NumberAdd(Arguments args) {
3616 NoHandleAllocation ha;
3617 ASSERT(args.length() == 2);
3618
3619 CONVERT_DOUBLE_CHECKED(x, args[0]);
3620 CONVERT_DOUBLE_CHECKED(y, args[1]);
3621 return Heap::AllocateHeapNumber(x + y);
3622}
3623
3624
3625static Object* Runtime_NumberSub(Arguments args) {
3626 NoHandleAllocation ha;
3627 ASSERT(args.length() == 2);
3628
3629 CONVERT_DOUBLE_CHECKED(x, args[0]);
3630 CONVERT_DOUBLE_CHECKED(y, args[1]);
3631 return Heap::AllocateHeapNumber(x - y);
3632}
3633
3634
3635static Object* Runtime_NumberMul(Arguments args) {
3636 NoHandleAllocation ha;
3637 ASSERT(args.length() == 2);
3638
3639 CONVERT_DOUBLE_CHECKED(x, args[0]);
3640 CONVERT_DOUBLE_CHECKED(y, args[1]);
3641 return Heap::AllocateHeapNumber(x * y);
3642}
3643
3644
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003645static Object* Runtime_NumberUnaryMinus(Arguments args) {
3646 NoHandleAllocation ha;
3647 ASSERT(args.length() == 1);
3648
3649 CONVERT_DOUBLE_CHECKED(x, args[0]);
3650 return Heap::AllocateHeapNumber(-x);
3651}
3652
3653
3654static Object* Runtime_NumberDiv(Arguments args) {
3655 NoHandleAllocation ha;
3656 ASSERT(args.length() == 2);
3657
3658 CONVERT_DOUBLE_CHECKED(x, args[0]);
3659 CONVERT_DOUBLE_CHECKED(y, args[1]);
3660 return Heap::NewNumberFromDouble(x / y);
3661}
3662
3663
3664static Object* Runtime_NumberMod(Arguments args) {
3665 NoHandleAllocation ha;
3666 ASSERT(args.length() == 2);
3667
3668 CONVERT_DOUBLE_CHECKED(x, args[0]);
3669 CONVERT_DOUBLE_CHECKED(y, args[1]);
3670
3671#ifdef WIN32
3672 // Workaround MS fmod bugs. ECMA-262 says:
3673 // dividend is finite and divisor is an infinity => result equals dividend
3674 // dividend is a zero and divisor is nonzero finite => result equals dividend
3675 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3676 !(x == 0 && (y != 0 && isfinite(y))))
3677#endif
3678 x = fmod(x, y);
3679 // NewNumberFromDouble may return a Smi instead of a Number object
3680 return Heap::NewNumberFromDouble(x);
3681}
3682
3683
3684static Object* Runtime_StringAdd(Arguments args) {
3685 NoHandleAllocation ha;
3686 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003687 CONVERT_CHECKED(String, str1, args[0]);
3688 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003689 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003690}
3691
3692
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003693template<typename sinkchar>
3694static inline void StringBuilderConcatHelper(String* special,
3695 sinkchar* sink,
3696 FixedArray* fixed_array,
3697 int array_length) {
3698 int position = 0;
3699 for (int i = 0; i < array_length; i++) {
3700 Object* element = fixed_array->get(i);
3701 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003702 int encoded_slice = Smi::cast(element)->value();
3703 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3704 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003705 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003706 sink + position,
3707 pos,
3708 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003709 position += len;
3710 } else {
3711 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003712 int element_length = string->length();
3713 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003714 position += element_length;
3715 }
3716 }
3717}
3718
3719
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003720static Object* Runtime_StringBuilderConcat(Arguments args) {
3721 NoHandleAllocation ha;
3722 ASSERT(args.length() == 2);
3723 CONVERT_CHECKED(JSArray, array, args[0]);
3724 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003725 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003726 Object* smi_array_length = array->length();
3727 if (!smi_array_length->IsSmi()) {
3728 Top::context()->mark_out_of_memory();
3729 return Failure::OutOfMemoryException();
3730 }
3731 int array_length = Smi::cast(smi_array_length)->value();
3732 if (!array->HasFastElements()) {
3733 return Top::Throw(Heap::illegal_argument_symbol());
3734 }
3735 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003736 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003737 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003738 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003739
3740 if (array_length == 0) {
3741 return Heap::empty_string();
3742 } else if (array_length == 1) {
3743 Object* first = fixed_array->get(0);
3744 if (first->IsString()) return first;
3745 }
3746
ager@chromium.org5ec48922009-05-05 07:25:34 +00003747 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003748 int position = 0;
3749 for (int i = 0; i < array_length; i++) {
3750 Object* elt = fixed_array->get(i);
3751 if (elt->IsSmi()) {
3752 int len = Smi::cast(elt)->value();
3753 int pos = len >> 11;
3754 len &= 0x7ff;
3755 if (pos + len > special_length) {
3756 return Top::Throw(Heap::illegal_argument_symbol());
3757 }
3758 position += len;
3759 } else if (elt->IsString()) {
3760 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003761 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003762 if (!Smi::IsValid(element_length + position)) {
3763 Top::context()->mark_out_of_memory();
3764 return Failure::OutOfMemoryException();
3765 }
3766 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003767 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003768 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003769 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003770 } else {
3771 return Top::Throw(Heap::illegal_argument_symbol());
3772 }
3773 }
3774
3775 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003776 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003777
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003778 if (ascii) {
3779 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003780 if (object->IsFailure()) return object;
3781 SeqAsciiString* answer = SeqAsciiString::cast(object);
3782 StringBuilderConcatHelper(special,
3783 answer->GetChars(),
3784 fixed_array,
3785 array_length);
3786 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003787 } else {
3788 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003789 if (object->IsFailure()) return object;
3790 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3791 StringBuilderConcatHelper(special,
3792 answer->GetChars(),
3793 fixed_array,
3794 array_length);
3795 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003796 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003797}
3798
3799
3800static Object* Runtime_NumberOr(Arguments args) {
3801 NoHandleAllocation ha;
3802 ASSERT(args.length() == 2);
3803
3804 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3805 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3806 return Heap::NumberFromInt32(x | y);
3807}
3808
3809
3810static Object* Runtime_NumberAnd(Arguments args) {
3811 NoHandleAllocation ha;
3812 ASSERT(args.length() == 2);
3813
3814 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3815 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3816 return Heap::NumberFromInt32(x & y);
3817}
3818
3819
3820static Object* Runtime_NumberXor(Arguments args) {
3821 NoHandleAllocation ha;
3822 ASSERT(args.length() == 2);
3823
3824 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3825 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3826 return Heap::NumberFromInt32(x ^ y);
3827}
3828
3829
3830static Object* Runtime_NumberNot(Arguments args) {
3831 NoHandleAllocation ha;
3832 ASSERT(args.length() == 1);
3833
3834 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3835 return Heap::NumberFromInt32(~x);
3836}
3837
3838
3839static Object* Runtime_NumberShl(Arguments args) {
3840 NoHandleAllocation ha;
3841 ASSERT(args.length() == 2);
3842
3843 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3844 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3845 return Heap::NumberFromInt32(x << (y & 0x1f));
3846}
3847
3848
3849static Object* Runtime_NumberShr(Arguments args) {
3850 NoHandleAllocation ha;
3851 ASSERT(args.length() == 2);
3852
3853 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3854 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3855 return Heap::NumberFromUint32(x >> (y & 0x1f));
3856}
3857
3858
3859static Object* Runtime_NumberSar(Arguments args) {
3860 NoHandleAllocation ha;
3861 ASSERT(args.length() == 2);
3862
3863 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3864 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3865 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3866}
3867
3868
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003869static Object* Runtime_NumberEquals(Arguments args) {
3870 NoHandleAllocation ha;
3871 ASSERT(args.length() == 2);
3872
3873 CONVERT_DOUBLE_CHECKED(x, args[0]);
3874 CONVERT_DOUBLE_CHECKED(y, args[1]);
3875 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3876 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3877 if (x == y) return Smi::FromInt(EQUAL);
3878 Object* result;
3879 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3880 result = Smi::FromInt(EQUAL);
3881 } else {
3882 result = Smi::FromInt(NOT_EQUAL);
3883 }
3884 return result;
3885}
3886
3887
3888static Object* Runtime_StringEquals(Arguments args) {
3889 NoHandleAllocation ha;
3890 ASSERT(args.length() == 2);
3891
3892 CONVERT_CHECKED(String, x, args[0]);
3893 CONVERT_CHECKED(String, y, args[1]);
3894
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003895 bool not_equal = !x->Equals(y);
3896 // This is slightly convoluted because the value that signifies
3897 // equality is 0 and inequality is 1 so we have to negate the result
3898 // from String::Equals.
3899 ASSERT(not_equal == 0 || not_equal == 1);
3900 STATIC_CHECK(EQUAL == 0);
3901 STATIC_CHECK(NOT_EQUAL == 1);
3902 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003903}
3904
3905
3906static Object* Runtime_NumberCompare(Arguments args) {
3907 NoHandleAllocation ha;
3908 ASSERT(args.length() == 3);
3909
3910 CONVERT_DOUBLE_CHECKED(x, args[0]);
3911 CONVERT_DOUBLE_CHECKED(y, args[1]);
3912 if (isnan(x) || isnan(y)) return args[2];
3913 if (x == y) return Smi::FromInt(EQUAL);
3914 if (isless(x, y)) return Smi::FromInt(LESS);
3915 return Smi::FromInt(GREATER);
3916}
3917
3918
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003919// Compare two Smis as if they were converted to strings and then
3920// compared lexicographically.
3921static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3922 NoHandleAllocation ha;
3923 ASSERT(args.length() == 2);
3924
3925 // Arrays for the individual characters of the two Smis. Smis are
3926 // 31 bit integers and 10 decimal digits are therefore enough.
3927 static int x_elms[10];
3928 static int y_elms[10];
3929
3930 // Extract the integer values from the Smis.
3931 CONVERT_CHECKED(Smi, x, args[0]);
3932 CONVERT_CHECKED(Smi, y, args[1]);
3933 int x_value = x->value();
3934 int y_value = y->value();
3935
3936 // If the integers are equal so are the string representations.
3937 if (x_value == y_value) return Smi::FromInt(EQUAL);
3938
3939 // If one of the integers are zero the normal integer order is the
3940 // same as the lexicographic order of the string representations.
3941 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3942
ager@chromium.org32912102009-01-16 10:38:43 +00003943 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003944 // smallest because the char code of '-' is less than the char code
3945 // of any digit. Otherwise, we make both values positive.
3946 if (x_value < 0 || y_value < 0) {
3947 if (y_value >= 0) return Smi::FromInt(LESS);
3948 if (x_value >= 0) return Smi::FromInt(GREATER);
3949 x_value = -x_value;
3950 y_value = -y_value;
3951 }
3952
3953 // Convert the integers to arrays of their decimal digits.
3954 int x_index = 0;
3955 int y_index = 0;
3956 while (x_value > 0) {
3957 x_elms[x_index++] = x_value % 10;
3958 x_value /= 10;
3959 }
3960 while (y_value > 0) {
3961 y_elms[y_index++] = y_value % 10;
3962 y_value /= 10;
3963 }
3964
3965 // Loop through the arrays of decimal digits finding the first place
3966 // where they differ.
3967 while (--x_index >= 0 && --y_index >= 0) {
3968 int diff = x_elms[x_index] - y_elms[y_index];
3969 if (diff != 0) return Smi::FromInt(diff);
3970 }
3971
3972 // If one array is a suffix of the other array, the longest array is
3973 // the representation of the largest of the Smis in the
3974 // lexicographic ordering.
3975 return Smi::FromInt(x_index - y_index);
3976}
3977
3978
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003979static Object* Runtime_StringCompare(Arguments args) {
3980 NoHandleAllocation ha;
3981 ASSERT(args.length() == 2);
3982
3983 CONVERT_CHECKED(String, x, args[0]);
3984 CONVERT_CHECKED(String, y, args[1]);
3985
3986 // A few fast case tests before we flatten.
3987 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003988 if (y->length() == 0) {
3989 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003990 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003991 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003992 return Smi::FromInt(LESS);
3993 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003994
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003995 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003996 if (d < 0) return Smi::FromInt(LESS);
3997 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003998
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003999 x->TryFlattenIfNotFlat();
4000 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004001
4002 static StringInputBuffer bufx;
4003 static StringInputBuffer bufy;
4004 bufx.Reset(x);
4005 bufy.Reset(y);
4006 while (bufx.has_more() && bufy.has_more()) {
4007 int d = bufx.GetNext() - bufy.GetNext();
4008 if (d < 0) return Smi::FromInt(LESS);
4009 else if (d > 0) return Smi::FromInt(GREATER);
4010 }
4011
4012 // x is (non-trivial) prefix of y:
4013 if (bufy.has_more()) return Smi::FromInt(LESS);
4014 // y is prefix of x:
4015 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4016}
4017
4018
4019static Object* Runtime_Math_abs(Arguments args) {
4020 NoHandleAllocation ha;
4021 ASSERT(args.length() == 1);
4022
4023 CONVERT_DOUBLE_CHECKED(x, args[0]);
4024 return Heap::AllocateHeapNumber(fabs(x));
4025}
4026
4027
4028static Object* Runtime_Math_acos(Arguments args) {
4029 NoHandleAllocation ha;
4030 ASSERT(args.length() == 1);
4031
4032 CONVERT_DOUBLE_CHECKED(x, args[0]);
4033 return Heap::AllocateHeapNumber(acos(x));
4034}
4035
4036
4037static Object* Runtime_Math_asin(Arguments args) {
4038 NoHandleAllocation ha;
4039 ASSERT(args.length() == 1);
4040
4041 CONVERT_DOUBLE_CHECKED(x, args[0]);
4042 return Heap::AllocateHeapNumber(asin(x));
4043}
4044
4045
4046static Object* Runtime_Math_atan(Arguments args) {
4047 NoHandleAllocation ha;
4048 ASSERT(args.length() == 1);
4049
4050 CONVERT_DOUBLE_CHECKED(x, args[0]);
4051 return Heap::AllocateHeapNumber(atan(x));
4052}
4053
4054
4055static Object* Runtime_Math_atan2(Arguments args) {
4056 NoHandleAllocation ha;
4057 ASSERT(args.length() == 2);
4058
4059 CONVERT_DOUBLE_CHECKED(x, args[0]);
4060 CONVERT_DOUBLE_CHECKED(y, args[1]);
4061 double result;
4062 if (isinf(x) && isinf(y)) {
4063 // Make sure that the result in case of two infinite arguments
4064 // is a multiple of Pi / 4. The sign of the result is determined
4065 // by the first argument (x) and the sign of the second argument
4066 // determines the multiplier: one or three.
4067 static double kPiDividedBy4 = 0.78539816339744830962;
4068 int multiplier = (x < 0) ? -1 : 1;
4069 if (y < 0) multiplier *= 3;
4070 result = multiplier * kPiDividedBy4;
4071 } else {
4072 result = atan2(x, y);
4073 }
4074 return Heap::AllocateHeapNumber(result);
4075}
4076
4077
4078static Object* Runtime_Math_ceil(Arguments args) {
4079 NoHandleAllocation ha;
4080 ASSERT(args.length() == 1);
4081
4082 CONVERT_DOUBLE_CHECKED(x, args[0]);
4083 return Heap::NumberFromDouble(ceiling(x));
4084}
4085
4086
4087static Object* Runtime_Math_cos(Arguments args) {
4088 NoHandleAllocation ha;
4089 ASSERT(args.length() == 1);
4090
4091 CONVERT_DOUBLE_CHECKED(x, args[0]);
4092 return Heap::AllocateHeapNumber(cos(x));
4093}
4094
4095
4096static Object* Runtime_Math_exp(Arguments args) {
4097 NoHandleAllocation ha;
4098 ASSERT(args.length() == 1);
4099
4100 CONVERT_DOUBLE_CHECKED(x, args[0]);
4101 return Heap::AllocateHeapNumber(exp(x));
4102}
4103
4104
4105static Object* Runtime_Math_floor(Arguments args) {
4106 NoHandleAllocation ha;
4107 ASSERT(args.length() == 1);
4108
4109 CONVERT_DOUBLE_CHECKED(x, args[0]);
4110 return Heap::NumberFromDouble(floor(x));
4111}
4112
4113
4114static Object* Runtime_Math_log(Arguments args) {
4115 NoHandleAllocation ha;
4116 ASSERT(args.length() == 1);
4117
4118 CONVERT_DOUBLE_CHECKED(x, args[0]);
4119 return Heap::AllocateHeapNumber(log(x));
4120}
4121
4122
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004123// Helper function to compute x^y, where y is known to be an
4124// integer. Uses binary decomposition to limit the number of
4125// multiplications; see the discussion in "Hacker's Delight" by Henry
4126// S. Warren, Jr., figure 11-6, page 213.
4127static double powi(double x, int y) {
4128 ASSERT(y != kMinInt);
4129 unsigned n = (y < 0) ? -y : y;
4130 double m = x;
4131 double p = 1;
4132 while (true) {
4133 if ((n & 1) != 0) p *= m;
4134 n >>= 1;
4135 if (n == 0) {
4136 if (y < 0) {
4137 // Unfortunately, we have to be careful when p has reached
4138 // infinity in the computation, because sometimes the higher
4139 // internal precision in the pow() implementation would have
4140 // given us a finite p. This happens very rarely.
4141 double result = 1.0 / p;
4142 return (result == 0 && isinf(p))
4143 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4144 : result;
4145 } else {
4146 return p;
4147 }
4148 }
4149 m *= m;
4150 }
4151}
4152
4153
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004154static Object* Runtime_Math_pow(Arguments args) {
4155 NoHandleAllocation ha;
4156 ASSERT(args.length() == 2);
4157
4158 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004159
4160 // If the second argument is a smi, it is much faster to call the
4161 // custom powi() function than the generic pow().
4162 if (args[1]->IsSmi()) {
4163 int y = Smi::cast(args[1])->value();
4164 return Heap::AllocateHeapNumber(powi(x, y));
4165 }
4166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004167 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004168
4169 if (!isinf(x)) {
4170 if (y == 0.5) {
4171 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4172 // square root of a number. To speed up such computations, we
4173 // explictly check for this case and use the sqrt() function
4174 // which is faster than pow().
4175 return Heap::AllocateHeapNumber(sqrt(x));
4176 } else if (y == -0.5) {
4177 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4178 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4179 }
4180 }
4181
4182 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004183 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004184 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4185 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004186 } else {
4187 return Heap::AllocateHeapNumber(pow(x, y));
4188 }
4189}
4190
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004191
4192static Object* Runtime_Math_round(Arguments args) {
4193 NoHandleAllocation ha;
4194 ASSERT(args.length() == 1);
4195
4196 CONVERT_DOUBLE_CHECKED(x, args[0]);
4197 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4198 return Heap::NumberFromDouble(floor(x + 0.5));
4199}
4200
4201
4202static Object* Runtime_Math_sin(Arguments args) {
4203 NoHandleAllocation ha;
4204 ASSERT(args.length() == 1);
4205
4206 CONVERT_DOUBLE_CHECKED(x, args[0]);
4207 return Heap::AllocateHeapNumber(sin(x));
4208}
4209
4210
4211static Object* Runtime_Math_sqrt(Arguments args) {
4212 NoHandleAllocation ha;
4213 ASSERT(args.length() == 1);
4214
4215 CONVERT_DOUBLE_CHECKED(x, args[0]);
4216 return Heap::AllocateHeapNumber(sqrt(x));
4217}
4218
4219
4220static Object* Runtime_Math_tan(Arguments args) {
4221 NoHandleAllocation ha;
4222 ASSERT(args.length() == 1);
4223
4224 CONVERT_DOUBLE_CHECKED(x, args[0]);
4225 return Heap::AllocateHeapNumber(tan(x));
4226}
4227
4228
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004229// The NewArguments function is only used when constructing the
4230// arguments array when calling non-functions from JavaScript in
4231// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004232static Object* Runtime_NewArguments(Arguments args) {
4233 NoHandleAllocation ha;
4234 ASSERT(args.length() == 1);
4235
4236 // ECMA-262, 3rd., 10.1.8, p.39
4237 CONVERT_CHECKED(JSFunction, callee, args[0]);
4238
4239 // Compute the frame holding the arguments.
4240 JavaScriptFrameIterator it;
4241 it.AdvanceToArgumentsFrame();
4242 JavaScriptFrame* frame = it.frame();
4243
4244 const int length = frame->GetProvidedParametersCount();
4245 Object* result = Heap::AllocateArgumentsObject(callee, length);
4246 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004247 if (length > 0) {
4248 Object* obj = Heap::AllocateFixedArray(length);
4249 if (obj->IsFailure()) return obj;
4250 FixedArray* array = FixedArray::cast(obj);
4251 ASSERT(array->length() == length);
4252 WriteBarrierMode mode = array->GetWriteBarrierMode();
4253 for (int i = 0; i < length; i++) {
4254 array->set(i, frame->GetParameter(i), mode);
4255 }
4256 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004257 }
4258 return result;
4259}
4260
4261
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004262static Object* Runtime_NewArgumentsFast(Arguments args) {
4263 NoHandleAllocation ha;
4264 ASSERT(args.length() == 3);
4265
4266 JSFunction* callee = JSFunction::cast(args[0]);
4267 Object** parameters = reinterpret_cast<Object**>(args[1]);
4268 const int length = Smi::cast(args[2])->value();
4269
4270 Object* result = Heap::AllocateArgumentsObject(callee, length);
4271 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004272 ASSERT(Heap::InNewSpace(result));
4273
4274 // Allocate the elements if needed.
4275 if (length > 0) {
4276 // Allocate the fixed array.
4277 Object* obj = Heap::AllocateRawFixedArray(length);
4278 if (obj->IsFailure()) return obj;
4279 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4280 FixedArray* array = FixedArray::cast(obj);
4281 array->set_length(length);
4282 WriteBarrierMode mode = array->GetWriteBarrierMode();
4283 for (int i = 0; i < length; i++) {
4284 array->set(i, *--parameters, mode);
4285 }
4286 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4287 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004288 }
4289 return result;
4290}
4291
4292
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004293static Object* Runtime_NewClosure(Arguments args) {
4294 HandleScope scope;
4295 ASSERT(args.length() == 2);
4296 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4297 CONVERT_ARG_CHECKED(Context, context, 1);
4298
4299 Handle<JSFunction> result =
4300 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4301 return *result;
4302}
4303
4304
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004305static Handle<Code> ComputeConstructStub(Handle<Map> map) {
4306 // TODO(385): Change this to create a construct stub specialized for
4307 // the given map to make allocation of simple objects - and maybe
4308 // arrays - much faster.
4309 return Handle<Code>(Builtins::builtin(Builtins::JSConstructStubGeneric));
4310}
4311
4312
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004313static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004314 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004315 ASSERT(args.length() == 1);
4316
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004317 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004318
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004319 // If the constructor isn't a proper function we throw a type error.
4320 if (!constructor->IsJSFunction()) {
4321 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4322 Handle<Object> type_error =
4323 Factory::NewTypeError("not_constructor", arguments);
4324 return Top::Throw(*type_error);
4325 }
4326
4327 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004328#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004329 // Handle stepping into constructors if step into is active.
4330 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004331 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004332 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004333#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004334
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004335 if (function->has_initial_map()) {
4336 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004337 // The 'Function' function ignores the receiver object when
4338 // called using 'new' and creates a new JSFunction object that
4339 // is returned. The receiver object is only used for error
4340 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004341 // JSFunction. Factory::NewJSObject() should not be used to
4342 // allocate JSFunctions since it does not properly initialize
4343 // the shared part of the function. Since the receiver is
4344 // ignored anyway, we use the global object as the receiver
4345 // instead of a new JSFunction object. This way, errors are
4346 // reported the same way whether or not 'Function' is called
4347 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004348 return Top::context()->global();
4349 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004350 }
4351
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004352 bool first_allocation = !function->has_initial_map();
4353 Handle<JSObject> result = Factory::NewJSObject(function);
4354 if (first_allocation) {
4355 Handle<Map> map = Handle<Map>(function->initial_map());
4356 Handle<Code> stub = ComputeConstructStub(map);
4357 function->shared()->set_construct_stub(*stub);
4358 }
4359 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004360}
4361
4362
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004363static Object* Runtime_LazyCompile(Arguments args) {
4364 HandleScope scope;
4365 ASSERT(args.length() == 1);
4366
4367 Handle<JSFunction> function = args.at<JSFunction>(0);
4368#ifdef DEBUG
4369 if (FLAG_trace_lazy) {
4370 PrintF("[lazy: ");
4371 function->shared()->name()->Print();
4372 PrintF("]\n");
4373 }
4374#endif
4375
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004376 // Compile the target function. Here we compile using CompileLazyInLoop in
4377 // order to get the optimized version. This helps code like delta-blue
4378 // that calls performance-critical routines through constructors. A
4379 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4380 // direct call. Since the in-loop tracking takes place through CallICs
4381 // this means that things called through constructors are never known to
4382 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004383 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004384 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004385 return Failure::Exception();
4386 }
4387
4388 return function->code();
4389}
4390
4391
4392static Object* Runtime_GetCalledFunction(Arguments args) {
4393 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004394 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004395 StackFrameIterator it;
4396 // Get past the JS-to-C exit frame.
4397 ASSERT(it.frame()->is_exit());
4398 it.Advance();
4399 // Get past the CALL_NON_FUNCTION activation frame.
4400 ASSERT(it.frame()->is_java_script());
4401 it.Advance();
4402 // Argument adaptor frames do not copy the function; we have to skip
4403 // past them to get to the real calling frame.
4404 if (it.frame()->is_arguments_adaptor()) it.Advance();
4405 // Get the function from the top of the expression stack of the
4406 // calling frame.
4407 StandardFrame* frame = StandardFrame::cast(it.frame());
4408 int index = frame->ComputeExpressionsCount() - 1;
4409 Object* result = frame->GetExpression(index);
4410 return result;
4411}
4412
4413
4414static Object* Runtime_GetFunctionDelegate(Arguments args) {
4415 HandleScope scope;
4416 ASSERT(args.length() == 1);
4417 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4418 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4419}
4420
4421
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004422static Object* Runtime_GetConstructorDelegate(Arguments args) {
4423 HandleScope scope;
4424 ASSERT(args.length() == 1);
4425 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4426 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4427}
4428
4429
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004430static Object* Runtime_NewContext(Arguments args) {
4431 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004432 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004433
kasper.lund7276f142008-07-30 08:49:36 +00004434 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004435 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4436 Object* result = Heap::AllocateFunctionContext(length, function);
4437 if (result->IsFailure()) return result;
4438
4439 Top::set_context(Context::cast(result));
4440
kasper.lund7276f142008-07-30 08:49:36 +00004441 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004442}
4443
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004444static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004445 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004446 Object* js_object = object;
4447 if (!js_object->IsJSObject()) {
4448 js_object = js_object->ToObject();
4449 if (js_object->IsFailure()) {
4450 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004451 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004452 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004453 Handle<Object> result =
4454 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4455 return Top::Throw(*result);
4456 }
4457 }
4458
4459 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004460 Heap::AllocateWithContext(Top::context(),
4461 JSObject::cast(js_object),
4462 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004463 if (result->IsFailure()) return result;
4464
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004465 Context* context = Context::cast(result);
4466 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004467
kasper.lund7276f142008-07-30 08:49:36 +00004468 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004469}
4470
4471
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004472static Object* Runtime_PushContext(Arguments args) {
4473 NoHandleAllocation ha;
4474 ASSERT(args.length() == 1);
4475 return PushContextHelper(args[0], false);
4476}
4477
4478
4479static Object* Runtime_PushCatchContext(Arguments args) {
4480 NoHandleAllocation ha;
4481 ASSERT(args.length() == 1);
4482 return PushContextHelper(args[0], true);
4483}
4484
4485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004486static Object* Runtime_LookupContext(Arguments args) {
4487 HandleScope scope;
4488 ASSERT(args.length() == 2);
4489
4490 CONVERT_ARG_CHECKED(Context, context, 0);
4491 CONVERT_ARG_CHECKED(String, name, 1);
4492
4493 int index;
4494 PropertyAttributes attributes;
4495 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004496 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004497 context->Lookup(name, flags, &index, &attributes);
4498
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004499 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004500 ASSERT(holder->IsJSObject());
4501 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004502 }
4503
4504 // No intermediate context found. Use global object by default.
4505 return Top::context()->global();
4506}
4507
4508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004509// A mechanism to return pairs of Object*'s. This is somewhat
4510// compiler-dependent as it assumes that a 64-bit value (a long long)
4511// is returned via two registers (edx:eax on ia32). Both the ia32 and
4512// arm platform support this; it is mostly an issue of "coaxing" the
4513// compiler to do the right thing.
4514//
4515// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004516#ifdef V8_HOST_ARCH_64_BIT
4517// Tested with GCC, not with MSVC.
4518struct ObjectPair {
4519 Object* x;
4520 Object* y;
4521};
4522static inline ObjectPair MakePair(Object* x, Object* y) {
4523 ObjectPair result = {x, y};
4524 return result; // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4525}
4526#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004527typedef uint64_t ObjectPair;
4528static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004529 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004530 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004531}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004532#endif
4533
4534
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004535
4536
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004537static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4539 USE(attributes);
4540 return x->IsTheHole() ? Heap::undefined_value() : x;
4541}
4542
4543
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004544static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4545 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004546 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004547 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004548 JSFunction* context_extension_function =
4549 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004550 // If the holder isn't a context extension object, we just return it
4551 // as the receiver. This allows arguments objects to be used as
4552 // receivers, but only if they are put in the context scope chain
4553 // explicitly via a with-statement.
4554 Object* constructor = holder->map()->constructor();
4555 if (constructor != context_extension_function) return holder;
4556 // Fall back to using the global object as the receiver if the
4557 // property turns out to be a local variable allocated in a context
4558 // extension object - introduced via eval.
4559 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004560}
4561
4562
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004563static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004564 HandleScope scope;
4565 ASSERT(args.length() == 2);
4566
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004567 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004568 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004569 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004570 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004571 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004572
4573 int index;
4574 PropertyAttributes attributes;
4575 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004576 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004577 context->Lookup(name, flags, &index, &attributes);
4578
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004579 // If the index is non-negative, the slot has been found in a local
4580 // variable or a parameter. Read it from the context object or the
4581 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004582 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004583 // If the "property" we were looking for is a local variable or an
4584 // argument in a context, the receiver is the global object; see
4585 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4586 JSObject* receiver = Top::context()->global()->global_receiver();
4587 Object* value = (holder->IsContext())
4588 ? Context::cast(*holder)->get(index)
4589 : JSObject::cast(*holder)->GetElement(index);
4590 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591 }
4592
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004593 // If the holder is found, we read the property from it.
4594 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004595 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004596 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004597 JSObject* receiver;
4598 if (object->IsGlobalObject()) {
4599 receiver = GlobalObject::cast(object)->global_receiver();
4600 } else if (context->is_exception_holder(*holder)) {
4601 receiver = Top::context()->global()->global_receiver();
4602 } else {
4603 receiver = ComputeReceiverForNonGlobal(object);
4604 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004605 // No need to unhole the value here. This is taken care of by the
4606 // GetProperty function.
4607 Object* value = object->GetProperty(*name);
4608 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004609 }
4610
4611 if (throw_error) {
4612 // The property doesn't exist - throw exception.
4613 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004614 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004615 return MakePair(Top::Throw(*reference_error), NULL);
4616 } else {
4617 // The property doesn't exist - return undefined
4618 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4619 }
4620}
4621
4622
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004623static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004624 return LoadContextSlotHelper(args, true);
4625}
4626
4627
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004628static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004629 return LoadContextSlotHelper(args, false);
4630}
4631
4632
4633static Object* Runtime_StoreContextSlot(Arguments args) {
4634 HandleScope scope;
4635 ASSERT(args.length() == 3);
4636
4637 Handle<Object> value(args[0]);
4638 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004639 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004640
4641 int index;
4642 PropertyAttributes attributes;
4643 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004644 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004645 context->Lookup(name, flags, &index, &attributes);
4646
4647 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004648 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004649 // Ignore if read_only variable.
4650 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004651 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004652 }
4653 } else {
4654 ASSERT((attributes & READ_ONLY) == 0);
4655 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004656 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004657 USE(result);
4658 ASSERT(!result->IsFailure());
4659 }
4660 return *value;
4661 }
4662
4663 // Slow case: The property is not in a FixedArray context.
4664 // It is either in an JSObject extension context or it was not found.
4665 Handle<JSObject> context_ext;
4666
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004667 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004668 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004669 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004670 } else {
4671 // The property was not found. It needs to be stored in the global context.
4672 ASSERT(attributes == ABSENT);
4673 attributes = NONE;
4674 context_ext = Handle<JSObject>(Top::context()->global());
4675 }
4676
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004677 // Set the property, but ignore if read_only variable on the context
4678 // extension object itself.
4679 if ((attributes & READ_ONLY) == 0 ||
4680 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004681 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4682 if (set.is_null()) {
4683 // Failure::Exception is converted to a null handle in the
4684 // handle-based methods such as SetProperty. We therefore need
4685 // to convert null handles back to exceptions.
4686 ASSERT(Top::has_pending_exception());
4687 return Failure::Exception();
4688 }
4689 }
4690 return *value;
4691}
4692
4693
4694static Object* Runtime_Throw(Arguments args) {
4695 HandleScope scope;
4696 ASSERT(args.length() == 1);
4697
4698 return Top::Throw(args[0]);
4699}
4700
4701
4702static Object* Runtime_ReThrow(Arguments args) {
4703 HandleScope scope;
4704 ASSERT(args.length() == 1);
4705
4706 return Top::ReThrow(args[0]);
4707}
4708
4709
4710static Object* Runtime_ThrowReferenceError(Arguments args) {
4711 HandleScope scope;
4712 ASSERT(args.length() == 1);
4713
4714 Handle<Object> name(args[0]);
4715 Handle<Object> reference_error =
4716 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4717 return Top::Throw(*reference_error);
4718}
4719
4720
4721static Object* Runtime_StackOverflow(Arguments args) {
4722 NoHandleAllocation na;
4723 return Top::StackOverflow();
4724}
4725
4726
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004727static Object* Runtime_StackGuard(Arguments args) {
4728 ASSERT(args.length() == 1);
4729
4730 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004731 if (StackGuard::IsStackOverflow()) {
4732 return Runtime_StackOverflow(args);
4733 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004734
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004735 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004736}
4737
4738
4739// NOTE: These PrintXXX functions are defined for all builds (not just
4740// DEBUG builds) because we may want to be able to trace function
4741// calls in all modes.
4742static void PrintString(String* str) {
4743 // not uncommon to have empty strings
4744 if (str->length() > 0) {
4745 SmartPointer<char> s =
4746 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4747 PrintF("%s", *s);
4748 }
4749}
4750
4751
4752static void PrintObject(Object* obj) {
4753 if (obj->IsSmi()) {
4754 PrintF("%d", Smi::cast(obj)->value());
4755 } else if (obj->IsString() || obj->IsSymbol()) {
4756 PrintString(String::cast(obj));
4757 } else if (obj->IsNumber()) {
4758 PrintF("%g", obj->Number());
4759 } else if (obj->IsFailure()) {
4760 PrintF("<failure>");
4761 } else if (obj->IsUndefined()) {
4762 PrintF("<undefined>");
4763 } else if (obj->IsNull()) {
4764 PrintF("<null>");
4765 } else if (obj->IsTrue()) {
4766 PrintF("<true>");
4767 } else if (obj->IsFalse()) {
4768 PrintF("<false>");
4769 } else {
4770 PrintF("%p", obj);
4771 }
4772}
4773
4774
4775static int StackSize() {
4776 int n = 0;
4777 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4778 return n;
4779}
4780
4781
4782static void PrintTransition(Object* result) {
4783 // indentation
4784 { const int nmax = 80;
4785 int n = StackSize();
4786 if (n <= nmax)
4787 PrintF("%4d:%*s", n, n, "");
4788 else
4789 PrintF("%4d:%*s", n, nmax, "...");
4790 }
4791
4792 if (result == NULL) {
4793 // constructor calls
4794 JavaScriptFrameIterator it;
4795 JavaScriptFrame* frame = it.frame();
4796 if (frame->IsConstructor()) PrintF("new ");
4797 // function name
4798 Object* fun = frame->function();
4799 if (fun->IsJSFunction()) {
4800 PrintObject(JSFunction::cast(fun)->shared()->name());
4801 } else {
4802 PrintObject(fun);
4803 }
4804 // function arguments
4805 // (we are intentionally only printing the actually
4806 // supplied parameters, not all parameters required)
4807 PrintF("(this=");
4808 PrintObject(frame->receiver());
4809 const int length = frame->GetProvidedParametersCount();
4810 for (int i = 0; i < length; i++) {
4811 PrintF(", ");
4812 PrintObject(frame->GetParameter(i));
4813 }
4814 PrintF(") {\n");
4815
4816 } else {
4817 // function result
4818 PrintF("} -> ");
4819 PrintObject(result);
4820 PrintF("\n");
4821 }
4822}
4823
4824
4825static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004826 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004827 NoHandleAllocation ha;
4828 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004829 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004830}
4831
4832
4833static Object* Runtime_TraceExit(Arguments args) {
4834 NoHandleAllocation ha;
4835 PrintTransition(args[0]);
4836 return args[0]; // return TOS
4837}
4838
4839
4840static Object* Runtime_DebugPrint(Arguments args) {
4841 NoHandleAllocation ha;
4842 ASSERT(args.length() == 1);
4843
4844#ifdef DEBUG
4845 if (args[0]->IsString()) {
4846 // If we have a string, assume it's a code "marker"
4847 // and print some interesting cpu debugging info.
4848 JavaScriptFrameIterator it;
4849 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004850 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4851 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852 } else {
4853 PrintF("DebugPrint: ");
4854 }
4855 args[0]->Print();
4856#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004857 // ShortPrint is available in release mode. Print is not.
4858 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004859#endif
4860 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004861 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004862
4863 return args[0]; // return TOS
4864}
4865
4866
4867static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004868 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004869 NoHandleAllocation ha;
4870 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004871 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004872}
4873
4874
mads.s.ager31e71382008-08-13 09:32:07 +00004875static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004876 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004877 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878
4879 // According to ECMA-262, section 15.9.1, page 117, the precision of
4880 // the number in a Date object representing a particular instant in
4881 // time is milliseconds. Therefore, we floor the result of getting
4882 // the OS time.
4883 double millis = floor(OS::TimeCurrentMillis());
4884 return Heap::NumberFromDouble(millis);
4885}
4886
4887
4888static Object* Runtime_DateParseString(Arguments args) {
4889 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004890 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004891
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004892 CONVERT_ARG_CHECKED(String, str, 0);
4893 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004894
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004895 CONVERT_ARG_CHECKED(JSArray, output, 1);
4896 RUNTIME_ASSERT(output->HasFastElements());
4897
4898 AssertNoAllocation no_allocation;
4899
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004900 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004901 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4902 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004903 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004904 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004905 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004906 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004907 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4908 }
4909
4910 if (result) {
4911 return *output;
4912 } else {
4913 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004914 }
4915}
4916
4917
4918static Object* Runtime_DateLocalTimezone(Arguments args) {
4919 NoHandleAllocation ha;
4920 ASSERT(args.length() == 1);
4921
4922 CONVERT_DOUBLE_CHECKED(x, args[0]);
4923 char* zone = OS::LocalTimezone(x);
4924 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4925}
4926
4927
4928static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4929 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004930 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004931
4932 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4933}
4934
4935
4936static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4937 NoHandleAllocation ha;
4938 ASSERT(args.length() == 1);
4939
4940 CONVERT_DOUBLE_CHECKED(x, args[0]);
4941 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4942}
4943
4944
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004945static Object* Runtime_NumberIsFinite(Arguments args) {
4946 NoHandleAllocation ha;
4947 ASSERT(args.length() == 1);
4948
4949 CONVERT_DOUBLE_CHECKED(value, args[0]);
4950 Object* result;
4951 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4952 result = Heap::false_value();
4953 } else {
4954 result = Heap::true_value();
4955 }
4956 return result;
4957}
4958
4959
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004960static Object* Runtime_GlobalReceiver(Arguments args) {
4961 ASSERT(args.length() == 1);
4962 Object* global = args[0];
4963 if (!global->IsJSGlobalObject()) return Heap::null_value();
4964 return JSGlobalObject::cast(global)->global_receiver();
4965}
4966
4967
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004968static Object* Runtime_CompileString(Arguments args) {
4969 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004970 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004971 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004972 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004973
ager@chromium.org381abbb2009-02-25 13:23:22 +00004974 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004975 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004976 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4977 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004978 true,
4979 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004980 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004981 Handle<JSFunction> fun =
4982 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4983 return *fun;
4984}
4985
4986
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004987static Handle<JSFunction> GetBuiltinFunction(String* name) {
4988 LookupResult result;
4989 Top::global_context()->builtins()->LocalLookup(name, &result);
4990 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4991}
4992
4993
4994static Object* CompileDirectEval(Handle<String> source) {
4995 // Compute the eval context.
4996 HandleScope scope;
4997 StackFrameLocator locator;
4998 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4999 Handle<Context> context(Context::cast(frame->context()));
5000 bool is_global = context->IsGlobalContext();
5001
ager@chromium.org381abbb2009-02-25 13:23:22 +00005002 // Compile source string in the current context.
5003 Handle<JSFunction> boilerplate =
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005004 Compiler::CompileEval(source, context, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005005 if (boilerplate.is_null()) return Failure::Exception();
5006 Handle<JSFunction> fun =
5007 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5008 return *fun;
5009}
5010
5011
5012static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5013 ASSERT(args.length() == 2);
5014
5015 HandleScope scope;
5016
5017 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5018
5019 Handle<Object> receiver;
5020
5021 // Find where the 'eval' symbol is bound. It is unaliased only if
5022 // it is bound in the global context.
5023 StackFrameLocator locator;
5024 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5025 Handle<Context> context(Context::cast(frame->context()));
5026 int index;
5027 PropertyAttributes attributes;
5028 while (!context.is_null()) {
5029 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5030 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005031 // Stop search when eval is found or when the global context is
5032 // reached.
5033 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005034 if (context->is_function_context()) {
5035 context = Handle<Context>(Context::cast(context->closure()->context()));
5036 } else {
5037 context = Handle<Context>(context->previous());
5038 }
5039 }
5040
iposva@chromium.org245aa852009-02-10 00:49:54 +00005041 // If eval could not be resolved, it has been deleted and we need to
5042 // throw a reference error.
5043 if (attributes == ABSENT) {
5044 Handle<Object> name = Factory::eval_symbol();
5045 Handle<Object> reference_error =
5046 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5047 return Top::Throw(*reference_error);
5048 }
5049
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005050 if (context->IsGlobalContext()) {
5051 // 'eval' is bound in the global context, but it may have been overwritten.
5052 // Compare it to the builtin 'GlobalEval' function to make sure.
5053 Handle<JSFunction> global_eval =
5054 GetBuiltinFunction(Heap::global_eval_symbol());
5055 if (global_eval.is_identical_to(callee)) {
5056 // A direct eval call.
5057 if (args[1]->IsString()) {
5058 CONVERT_ARG_CHECKED(String, source, 1);
5059 // A normal eval call on a string. Compile it and return the
5060 // compiled function bound in the local context.
5061 Object* compiled_source = CompileDirectEval(source);
5062 if (compiled_source->IsFailure()) return compiled_source;
5063 receiver = Handle<Object>(frame->receiver());
5064 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5065 } else {
5066 // An eval call that is not called on a string. Global eval
5067 // deals better with this.
5068 receiver = Handle<Object>(Top::global_context()->global());
5069 }
5070 } else {
5071 // 'eval' is overwritten. Just call the function with the given arguments.
5072 receiver = Handle<Object>(Top::global_context()->global());
5073 }
5074 } else {
5075 // 'eval' is not bound in the global context. Just call the function
5076 // with the given arguments. This is not necessarily the global eval.
5077 if (receiver->IsContext()) {
5078 context = Handle<Context>::cast(receiver);
5079 receiver = Handle<Object>(context->get(index));
5080 }
5081 }
5082
5083 Handle<FixedArray> call = Factory::NewFixedArray(2);
5084 call->set(0, *callee);
5085 call->set(1, *receiver);
5086 return *call;
5087}
5088
5089
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005090static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5091 // This utility adjusts the property attributes for newly created Function
5092 // object ("new Function(...)") by changing the map.
5093 // All it does is changing the prototype property to enumerable
5094 // as specified in ECMA262, 15.3.5.2.
5095 HandleScope scope;
5096 ASSERT(args.length() == 1);
5097 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5098 ASSERT(func->map()->instance_type() ==
5099 Top::function_instance_map()->instance_type());
5100 ASSERT(func->map()->instance_size() ==
5101 Top::function_instance_map()->instance_size());
5102 func->set_map(*Top::function_instance_map());
5103 return *func;
5104}
5105
5106
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005107// Push an array unto an array of arrays if it is not already in the
5108// array. Returns true if the element was pushed on the stack and
5109// false otherwise.
5110static Object* Runtime_PushIfAbsent(Arguments args) {
5111 ASSERT(args.length() == 2);
5112 CONVERT_CHECKED(JSArray, array, args[0]);
5113 CONVERT_CHECKED(JSArray, element, args[1]);
5114 RUNTIME_ASSERT(array->HasFastElements());
5115 int length = Smi::cast(array->length())->value();
5116 FixedArray* elements = FixedArray::cast(array->elements());
5117 for (int i = 0; i < length; i++) {
5118 if (elements->get(i) == element) return Heap::false_value();
5119 }
5120 Object* obj = array->SetFastElement(length, element);
5121 if (obj->IsFailure()) return obj;
5122 return Heap::true_value();
5123}
5124
5125
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005126/**
5127 * A simple visitor visits every element of Array's.
5128 * The backend storage can be a fixed array for fast elements case,
5129 * or a dictionary for sparse array. Since Dictionary is a subtype
5130 * of FixedArray, the class can be used by both fast and slow cases.
5131 * The second parameter of the constructor, fast_elements, specifies
5132 * whether the storage is a FixedArray or Dictionary.
5133 *
5134 * An index limit is used to deal with the situation that a result array
5135 * length overflows 32-bit non-negative integer.
5136 */
5137class ArrayConcatVisitor {
5138 public:
5139 ArrayConcatVisitor(Handle<FixedArray> storage,
5140 uint32_t index_limit,
5141 bool fast_elements) :
5142 storage_(storage), index_limit_(index_limit),
5143 fast_elements_(fast_elements), index_offset_(0) { }
5144
5145 void visit(uint32_t i, Handle<Object> elm) {
5146 uint32_t index = i + index_offset_;
5147 if (index >= index_limit_) return;
5148
5149 if (fast_elements_) {
5150 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5151 storage_->set(index, *elm);
5152
5153 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005154 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5155 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005156 Factory::DictionaryAtNumberPut(dict, index, elm);
5157 if (!result.is_identical_to(dict))
5158 storage_ = result;
5159 }
5160 }
5161
5162 void increase_index_offset(uint32_t delta) {
5163 index_offset_ += delta;
5164 }
5165
5166 private:
5167 Handle<FixedArray> storage_;
5168 uint32_t index_limit_;
5169 bool fast_elements_;
5170 uint32_t index_offset_;
5171};
5172
5173
5174/**
5175 * A helper function that visits elements of a JSObject. Only elements
5176 * whose index between 0 and range (exclusive) are visited.
5177 *
5178 * If the third parameter, visitor, is not NULL, the visitor is called
5179 * with parameters, 'visitor_index_offset + element index' and the element.
5180 *
5181 * It returns the number of visisted elements.
5182 */
5183static uint32_t IterateElements(Handle<JSObject> receiver,
5184 uint32_t range,
5185 ArrayConcatVisitor* visitor) {
5186 uint32_t num_of_elements = 0;
5187
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005188 switch (receiver->GetElementsKind()) {
5189 case JSObject::FAST_ELEMENTS: {
5190 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5191 uint32_t len = elements->length();
5192 if (range < len) {
5193 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005194 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005195
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005196 for (uint32_t j = 0; j < len; j++) {
5197 Handle<Object> e(elements->get(j));
5198 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005199 num_of_elements++;
5200 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005201 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005202 }
5203 }
5204 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005205 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005206 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005207 case JSObject::PIXEL_ELEMENTS: {
5208 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5209 uint32_t len = pixels->length();
5210 if (range < len) {
5211 len = range;
5212 }
5213
5214 for (uint32_t j = 0; j < len; j++) {
5215 num_of_elements++;
5216 if (visitor != NULL) {
5217 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5218 visitor->visit(j, e);
5219 }
5220 }
5221 break;
5222 }
5223 case JSObject::DICTIONARY_ELEMENTS: {
5224 Handle<NumberDictionary> dict(receiver->element_dictionary());
5225 uint32_t capacity = dict->Capacity();
5226 for (uint32_t j = 0; j < capacity; j++) {
5227 Handle<Object> k(dict->KeyAt(j));
5228 if (dict->IsKey(*k)) {
5229 ASSERT(k->IsNumber());
5230 uint32_t index = static_cast<uint32_t>(k->Number());
5231 if (index < range) {
5232 num_of_elements++;
5233 if (visitor) {
5234 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5235 }
5236 }
5237 }
5238 }
5239 break;
5240 }
5241 default:
5242 UNREACHABLE();
5243 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005244 }
5245
5246 return num_of_elements;
5247}
5248
5249
5250/**
5251 * A helper function that visits elements of an Array object, and elements
5252 * on its prototypes.
5253 *
5254 * Elements on prototypes are visited first, and only elements whose indices
5255 * less than Array length are visited.
5256 *
5257 * If a ArrayConcatVisitor object is given, the visitor is called with
5258 * parameters, element's index + visitor_index_offset and the element.
5259 */
5260static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5261 ArrayConcatVisitor* visitor) {
5262 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5263 Handle<Object> obj = array;
5264
5265 static const int kEstimatedPrototypes = 3;
5266 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5267
5268 // Visit prototype first. If an element on the prototype is shadowed by
5269 // the inheritor using the same index, the ArrayConcatVisitor visits
5270 // the prototype element before the shadowing element.
5271 // The visitor can simply overwrite the old value by new value using
5272 // the same index. This follows Array::concat semantics.
5273 while (!obj->IsNull()) {
5274 objects.Add(Handle<JSObject>::cast(obj));
5275 obj = Handle<Object>(obj->GetPrototype());
5276 }
5277
5278 uint32_t nof_elements = 0;
5279 for (int i = objects.length() - 1; i >= 0; i--) {
5280 Handle<JSObject> obj = objects[i];
5281 nof_elements +=
5282 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5283 }
5284
5285 return nof_elements;
5286}
5287
5288
5289/**
5290 * A helper function of Runtime_ArrayConcat.
5291 *
5292 * The first argument is an Array of arrays and objects. It is the
5293 * same as the arguments array of Array::concat JS function.
5294 *
5295 * If an argument is an Array object, the function visits array
5296 * elements. If an argument is not an Array object, the function
5297 * visits the object as if it is an one-element array.
5298 *
5299 * If the result array index overflows 32-bit integer, the rounded
5300 * non-negative number is used as new length. For example, if one
5301 * array length is 2^32 - 1, second array length is 1, the
5302 * concatenated array length is 0.
5303 */
5304static uint32_t IterateArguments(Handle<JSArray> arguments,
5305 ArrayConcatVisitor* visitor) {
5306 uint32_t visited_elements = 0;
5307 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5308
5309 for (uint32_t i = 0; i < num_of_args; i++) {
5310 Handle<Object> obj(arguments->GetElement(i));
5311 if (obj->IsJSArray()) {
5312 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5313 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5314 uint32_t nof_elements =
5315 IterateArrayAndPrototypeElements(array, visitor);
5316 // Total elements of array and its prototype chain can be more than
5317 // the array length, but ArrayConcat can only concatenate at most
5318 // the array length number of elements.
5319 visited_elements += (nof_elements > len) ? len : nof_elements;
5320 if (visitor) visitor->increase_index_offset(len);
5321
5322 } else {
5323 if (visitor) {
5324 visitor->visit(0, obj);
5325 visitor->increase_index_offset(1);
5326 }
5327 visited_elements++;
5328 }
5329 }
5330 return visited_elements;
5331}
5332
5333
5334/**
5335 * Array::concat implementation.
5336 * See ECMAScript 262, 15.4.4.4.
5337 */
5338static Object* Runtime_ArrayConcat(Arguments args) {
5339 ASSERT(args.length() == 1);
5340 HandleScope handle_scope;
5341
5342 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5343 Handle<JSArray> arguments(arg_arrays);
5344
5345 // Pass 1: estimate the number of elements of the result
5346 // (it could be more than real numbers if prototype has elements).
5347 uint32_t result_length = 0;
5348 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5349
5350 { AssertNoAllocation nogc;
5351 for (uint32_t i = 0; i < num_of_args; i++) {
5352 Object* obj = arguments->GetElement(i);
5353 if (obj->IsJSArray()) {
5354 result_length +=
5355 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5356 } else {
5357 result_length++;
5358 }
5359 }
5360 }
5361
5362 // Allocate an empty array, will set length and content later.
5363 Handle<JSArray> result = Factory::NewJSArray(0);
5364
5365 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5366 // If estimated number of elements is more than half of length, a
5367 // fixed array (fast case) is more time and space-efficient than a
5368 // dictionary.
5369 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5370
5371 Handle<FixedArray> storage;
5372 if (fast_case) {
5373 // The backing storage array must have non-existing elements to
5374 // preserve holes across concat operations.
5375 storage = Factory::NewFixedArrayWithHoles(result_length);
5376
5377 } else {
5378 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5379 uint32_t at_least_space_for = estimate_nof_elements +
5380 (estimate_nof_elements >> 2);
5381 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005382 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005383 }
5384
5385 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5386
5387 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5388
5389 IterateArguments(arguments, &visitor);
5390
5391 result->set_length(*len);
5392 result->set_elements(*storage);
5393
5394 return *result;
5395}
5396
5397
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005398// This will not allocate (flatten the string), but it may run
5399// very slowly for very deeply nested ConsStrings. For debugging use only.
5400static Object* Runtime_GlobalPrint(Arguments args) {
5401 NoHandleAllocation ha;
5402 ASSERT(args.length() == 1);
5403
5404 CONVERT_CHECKED(String, string, args[0]);
5405 StringInputBuffer buffer(string);
5406 while (buffer.has_more()) {
5407 uint16_t character = buffer.GetNext();
5408 PrintF("%c", character);
5409 }
5410 return string;
5411}
5412
ager@chromium.org5ec48922009-05-05 07:25:34 +00005413// Moves all own elements of an object, that are below a limit, to positions
5414// starting at zero. All undefined values are placed after non-undefined values,
5415// and are followed by non-existing element. Does not change the length
5416// property.
5417// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005418static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005419 ASSERT(args.length() == 2);
5420 CONVERT_CHECKED(JSObject, object, args[0]);
5421 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5422 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005423}
5424
5425
5426// Move contents of argument 0 (an array) to argument 1 (an array)
5427static Object* Runtime_MoveArrayContents(Arguments args) {
5428 ASSERT(args.length() == 2);
5429 CONVERT_CHECKED(JSArray, from, args[0]);
5430 CONVERT_CHECKED(JSArray, to, args[1]);
5431 to->SetContent(FixedArray::cast(from->elements()));
5432 to->set_length(from->length());
5433 from->SetContent(Heap::empty_fixed_array());
5434 from->set_length(0);
5435 return to;
5436}
5437
5438
5439// How many elements does this array have?
5440static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5441 ASSERT(args.length() == 1);
5442 CONVERT_CHECKED(JSArray, array, args[0]);
5443 HeapObject* elements = array->elements();
5444 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005445 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005446 } else {
5447 return array->length();
5448 }
5449}
5450
5451
5452// Returns an array that tells you where in the [0, length) interval an array
5453// might have elements. Can either return keys or intervals. Keys can have
5454// gaps in (undefined). Intervals can also span over some undefined keys.
5455static Object* Runtime_GetArrayKeys(Arguments args) {
5456 ASSERT(args.length() == 2);
5457 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005458 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005459 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005460 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005461 // Create an array and get all the keys into it, then remove all the
5462 // keys that are not integers in the range 0 to length-1.
5463 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5464 int keys_length = keys->length();
5465 for (int i = 0; i < keys_length; i++) {
5466 Object* key = keys->get(i);
5467 uint32_t index;
5468 if (!Array::IndexFromObject(key, &index) || index >= length) {
5469 // Zap invalid keys.
5470 keys->set_undefined(i);
5471 }
5472 }
5473 return *Factory::NewJSArrayWithElements(keys);
5474 } else {
5475 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5476 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005477 single_interval->set(0,
5478 Smi::FromInt(-1),
5479 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005480 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5481 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005482 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005483 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005484 single_interval->set(1, *length_object);
5485 return *Factory::NewJSArrayWithElements(single_interval);
5486 }
5487}
5488
5489
5490// DefineAccessor takes an optional final argument which is the
5491// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5492// to the way accessors are implemented, it is set for both the getter
5493// and setter on the first call to DefineAccessor and ignored on
5494// subsequent calls.
5495static Object* Runtime_DefineAccessor(Arguments args) {
5496 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5497 // Compute attributes.
5498 PropertyAttributes attributes = NONE;
5499 if (args.length() == 5) {
5500 CONVERT_CHECKED(Smi, attrs, args[4]);
5501 int value = attrs->value();
5502 // Only attribute bits should be set.
5503 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5504 attributes = static_cast<PropertyAttributes>(value);
5505 }
5506
5507 CONVERT_CHECKED(JSObject, obj, args[0]);
5508 CONVERT_CHECKED(String, name, args[1]);
5509 CONVERT_CHECKED(Smi, flag, args[2]);
5510 CONVERT_CHECKED(JSFunction, fun, args[3]);
5511 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5512}
5513
5514
5515static Object* Runtime_LookupAccessor(Arguments args) {
5516 ASSERT(args.length() == 3);
5517 CONVERT_CHECKED(JSObject, obj, args[0]);
5518 CONVERT_CHECKED(String, name, args[1]);
5519 CONVERT_CHECKED(Smi, flag, args[2]);
5520 return obj->LookupAccessor(name, flag->value() == 0);
5521}
5522
5523
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005524#ifdef ENABLE_DEBUGGER_SUPPORT
5525static Object* Runtime_DebugBreak(Arguments args) {
5526 ASSERT(args.length() == 0);
5527 return Execution::DebugBreakHelper();
5528}
5529
5530
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005531// Helper functions for wrapping and unwrapping stack frame ids.
5532static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005533 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005534 return Smi::FromInt(id >> 2);
5535}
5536
5537
5538static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5539 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5540}
5541
5542
5543// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005544// args[0]: debug event listener function to set or null or undefined for
5545// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005546// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005547static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005549 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5550 args[0]->IsUndefined() ||
5551 args[0]->IsNull());
5552 Handle<Object> callback = args.at<Object>(0);
5553 Handle<Object> data = args.at<Object>(1);
5554 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005555
5556 return Heap::undefined_value();
5557}
5558
5559
5560static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005561 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005562 StackGuard::DebugBreak();
5563 return Heap::undefined_value();
5564}
5565
5566
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005567// Find the length of the prototype chain that is to to handled as one. If a
5568// prototype object is hidden it is to be viewed as part of the the object it
5569// is prototype for.
5570static int LocalPrototypeChainLength(JSObject* obj) {
5571 int count = 1;
5572 Object* proto = obj->GetPrototype();
5573 while (proto->IsJSObject() &&
5574 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5575 count++;
5576 proto = JSObject::cast(proto)->GetPrototype();
5577 }
5578 return count;
5579}
5580
5581
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005582static Object* DebugLookupResultValue(Object* receiver, String* name,
5583 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005584 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005585 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005587 case NORMAL:
5588 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005589 if (value->IsTheHole()) {
5590 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005591 }
5592 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005593 case FIELD:
5594 value =
5595 JSObject::cast(
5596 result->holder())->FastPropertyAt(result->GetFieldIndex());
5597 if (value->IsTheHole()) {
5598 return Heap::undefined_value();
5599 }
5600 return value;
5601 case CONSTANT_FUNCTION:
5602 return result->GetConstantFunction();
5603 case CALLBACKS: {
5604 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005605 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005606 value = receiver->GetPropertyWithCallback(
5607 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005608 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005609 value = Top::pending_exception();
5610 Top::clear_pending_exception();
5611 if (caught_exception != NULL) {
5612 *caught_exception = true;
5613 }
5614 }
5615 return value;
5616 } else {
5617 return Heap::undefined_value();
5618 }
5619 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005620 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005621 case MAP_TRANSITION:
5622 case CONSTANT_TRANSITION:
5623 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005624 return Heap::undefined_value();
5625 default:
5626 UNREACHABLE();
5627 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005628 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005629 return Heap::undefined_value();
5630}
5631
5632
ager@chromium.org32912102009-01-16 10:38:43 +00005633// Get debugger related details for an object property.
5634// args[0]: object holding property
5635// args[1]: name of the property
5636//
5637// The array returned contains the following information:
5638// 0: Property value
5639// 1: Property details
5640// 2: Property value is exception
5641// 3: Getter function if defined
5642// 4: Setter function if defined
5643// Items 2-4 are only filled if the property has either a getter or a setter
5644// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005645static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005646 HandleScope scope;
5647
5648 ASSERT(args.length() == 2);
5649
5650 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5651 CONVERT_ARG_CHECKED(String, name, 1);
5652
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005653 // Make sure to set the current context to the context before the debugger was
5654 // entered (if the debugger is entered). The reason for switching context here
5655 // is that for some property lookups (accessors and interceptors) callbacks
5656 // into the embedding application can occour, and the embedding application
5657 // could have the assumption that its own global context is the current
5658 // context and not some internal debugger context.
5659 SaveContext save;
5660 if (Debug::InDebugger()) {
5661 Top::set_context(*Debug::debugger_entry()->GetContext());
5662 }
5663
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005664 // Skip the global proxy as it has no properties and always delegates to the
5665 // real global object.
5666 if (obj->IsJSGlobalProxy()) {
5667 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5668 }
5669
5670
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005671 // Check if the name is trivially convertible to an index and get the element
5672 // if so.
5673 uint32_t index;
5674 if (name->AsArrayIndex(&index)) {
5675 Handle<FixedArray> details = Factory::NewFixedArray(2);
5676 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5677 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5678 return *Factory::NewJSArrayWithElements(details);
5679 }
5680
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005681 // Find the number of objects making up this.
5682 int length = LocalPrototypeChainLength(*obj);
5683
5684 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005685 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005686 Handle<JSObject> jsproto = obj;
5687 for (int i = 0; i < length; i++) {
5688 jsproto->LocalLookup(*name, &result);
5689 if (result.IsProperty()) {
5690 break;
5691 }
5692 if (i < length - 1) {
5693 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5694 }
5695 }
5696
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005697 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005698 // LookupResult is not GC safe as all its members are raw object pointers.
5699 // When calling DebugLookupResultValue GC can happen as this might invoke
5700 // callbacks. After the call to DebugLookupResultValue the callback object
5701 // in the LookupResult might still be needed. Put it into a handle for later
5702 // use.
5703 PropertyType result_type = result.type();
5704 Handle<Object> result_callback_obj;
5705 if (result_type == CALLBACKS) {
5706 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5707 }
5708
5709 // Find the actual value. Don't use result after this call as it's content
5710 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005711 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005712 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005713 &caught_exception);
5714 if (value->IsFailure()) return value;
5715 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005716
ager@chromium.org32912102009-01-16 10:38:43 +00005717 // If the callback object is a fixed array then it contains JavaScript
5718 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005719 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5720 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005721 Handle<FixedArray> details =
5722 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005723 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005724 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005725 if (hasJavaScriptAccessors) {
5726 details->set(2,
5727 caught_exception ? Heap::true_value() : Heap::false_value());
5728 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5729 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5730 }
5731
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005732 return *Factory::NewJSArrayWithElements(details);
5733 }
5734 return Heap::undefined_value();
5735}
5736
5737
5738static Object* Runtime_DebugGetProperty(Arguments args) {
5739 HandleScope scope;
5740
5741 ASSERT(args.length() == 2);
5742
5743 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5744 CONVERT_ARG_CHECKED(String, name, 1);
5745
5746 LookupResult result;
5747 obj->Lookup(*name, &result);
5748 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005749 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005750 }
5751 return Heap::undefined_value();
5752}
5753
5754
5755// Return the names of the local named properties.
5756// args[0]: object
5757static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5758 HandleScope scope;
5759 ASSERT(args.length() == 1);
5760 if (!args[0]->IsJSObject()) {
5761 return Heap::undefined_value();
5762 }
5763 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5764
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005765 // Skip the global proxy as it has no properties and always delegates to the
5766 // real global object.
5767 if (obj->IsJSGlobalProxy()) {
5768 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5769 }
5770
5771 // Find the number of objects making up this.
5772 int length = LocalPrototypeChainLength(*obj);
5773
5774 // Find the number of local properties for each of the objects.
5775 int* local_property_count = NewArray<int>(length);
5776 int total_property_count = 0;
5777 Handle<JSObject> jsproto = obj;
5778 for (int i = 0; i < length; i++) {
5779 int n;
5780 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5781 local_property_count[i] = n;
5782 total_property_count += n;
5783 if (i < length - 1) {
5784 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5785 }
5786 }
5787
5788 // Allocate an array with storage for all the property names.
5789 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5790
5791 // Get the property names.
5792 jsproto = obj;
5793 for (int i = 0; i < length; i++) {
5794 jsproto->GetLocalPropertyNames(*names,
5795 i == 0 ? 0 : local_property_count[i - 1]);
5796 if (i < length - 1) {
5797 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5798 }
5799 }
5800
5801 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005802 return *Factory::NewJSArrayWithElements(names);
5803}
5804
5805
5806// Return the names of the local indexed properties.
5807// args[0]: object
5808static Object* Runtime_DebugLocalElementNames(Arguments args) {
5809 HandleScope scope;
5810 ASSERT(args.length() == 1);
5811 if (!args[0]->IsJSObject()) {
5812 return Heap::undefined_value();
5813 }
5814 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5815
5816 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5817 Handle<FixedArray> names = Factory::NewFixedArray(n);
5818 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5819 return *Factory::NewJSArrayWithElements(names);
5820}
5821
5822
5823// Return the property type calculated from the property details.
5824// args[0]: smi with property details.
5825static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5826 ASSERT(args.length() == 1);
5827 CONVERT_CHECKED(Smi, details, args[0]);
5828 PropertyType type = PropertyDetails(details).type();
5829 return Smi::FromInt(static_cast<int>(type));
5830}
5831
5832
5833// Return the property attribute calculated from the property details.
5834// args[0]: smi with property details.
5835static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5836 ASSERT(args.length() == 1);
5837 CONVERT_CHECKED(Smi, details, args[0]);
5838 PropertyAttributes attributes = PropertyDetails(details).attributes();
5839 return Smi::FromInt(static_cast<int>(attributes));
5840}
5841
5842
5843// Return the property insertion index calculated from the property details.
5844// args[0]: smi with property details.
5845static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5846 ASSERT(args.length() == 1);
5847 CONVERT_CHECKED(Smi, details, args[0]);
5848 int index = PropertyDetails(details).index();
5849 return Smi::FromInt(index);
5850}
5851
5852
5853// Return information on whether an object has a named or indexed interceptor.
5854// args[0]: object
5855static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5856 HandleScope scope;
5857 ASSERT(args.length() == 1);
5858 if (!args[0]->IsJSObject()) {
5859 return Smi::FromInt(0);
5860 }
5861 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5862
5863 int result = 0;
5864 if (obj->HasNamedInterceptor()) result |= 2;
5865 if (obj->HasIndexedInterceptor()) result |= 1;
5866
5867 return Smi::FromInt(result);
5868}
5869
5870
5871// Return property names from named interceptor.
5872// args[0]: object
5873static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5874 HandleScope scope;
5875 ASSERT(args.length() == 1);
5876 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005877
ager@chromium.org32912102009-01-16 10:38:43 +00005878 if (obj->HasNamedInterceptor()) {
5879 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5880 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5881 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005882 return Heap::undefined_value();
5883}
5884
5885
5886// Return element names from indexed interceptor.
5887// args[0]: object
5888static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5889 HandleScope scope;
5890 ASSERT(args.length() == 1);
5891 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005892
ager@chromium.org32912102009-01-16 10:38:43 +00005893 if (obj->HasIndexedInterceptor()) {
5894 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5895 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5896 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005897 return Heap::undefined_value();
5898}
5899
5900
5901// Return property value from named interceptor.
5902// args[0]: object
5903// args[1]: property name
5904static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5905 HandleScope scope;
5906 ASSERT(args.length() == 2);
5907 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5908 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5909 CONVERT_ARG_CHECKED(String, name, 1);
5910
5911 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005912 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005913}
5914
5915
5916// Return element value from indexed interceptor.
5917// args[0]: object
5918// args[1]: index
5919static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5920 HandleScope scope;
5921 ASSERT(args.length() == 2);
5922 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5923 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5924 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5925
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005926 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005927}
5928
5929
5930static Object* Runtime_CheckExecutionState(Arguments args) {
5931 ASSERT(args.length() >= 1);
5932 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005933 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005934 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005935 return Top::Throw(Heap::illegal_execution_state_symbol());
5936 }
5937
5938 return Heap::true_value();
5939}
5940
5941
5942static Object* Runtime_GetFrameCount(Arguments args) {
5943 HandleScope scope;
5944 ASSERT(args.length() == 1);
5945
5946 // Check arguments.
5947 Object* result = Runtime_CheckExecutionState(args);
5948 if (result->IsFailure()) return result;
5949
5950 // Count all frames which are relevant to debugging stack trace.
5951 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005952 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005953 if (id == StackFrame::NO_ID) {
5954 // If there is no JavaScript stack frame count is 0.
5955 return Smi::FromInt(0);
5956 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005957 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5958 return Smi::FromInt(n);
5959}
5960
5961
5962static const int kFrameDetailsFrameIdIndex = 0;
5963static const int kFrameDetailsReceiverIndex = 1;
5964static const int kFrameDetailsFunctionIndex = 2;
5965static const int kFrameDetailsArgumentCountIndex = 3;
5966static const int kFrameDetailsLocalCountIndex = 4;
5967static const int kFrameDetailsSourcePositionIndex = 5;
5968static const int kFrameDetailsConstructCallIndex = 6;
5969static const int kFrameDetailsDebuggerFrameIndex = 7;
5970static const int kFrameDetailsFirstDynamicIndex = 8;
5971
5972// Return an array with frame details
5973// args[0]: number: break id
5974// args[1]: number: frame index
5975//
5976// The array returned contains the following information:
5977// 0: Frame id
5978// 1: Receiver
5979// 2: Function
5980// 3: Argument count
5981// 4: Local count
5982// 5: Source position
5983// 6: Constructor call
5984// 7: Debugger frame
5985// Arguments name, value
5986// Locals name, value
5987static Object* Runtime_GetFrameDetails(Arguments args) {
5988 HandleScope scope;
5989 ASSERT(args.length() == 2);
5990
5991 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005992 Object* check = Runtime_CheckExecutionState(args);
5993 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005994 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5995
5996 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005997 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005998 if (id == StackFrame::NO_ID) {
5999 // If there are no JavaScript stack frames return undefined.
6000 return Heap::undefined_value();
6001 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006002 int count = 0;
6003 JavaScriptFrameIterator it(id);
6004 for (; !it.done(); it.Advance()) {
6005 if (count == index) break;
6006 count++;
6007 }
6008 if (it.done()) return Heap::undefined_value();
6009
6010 // Traverse the saved contexts chain to find the active context for the
6011 // selected frame.
6012 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006013 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006014 save = save->prev();
6015 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006016 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006017
6018 // Get the frame id.
6019 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6020
6021 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006022 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006023
6024 // Check for constructor frame.
6025 bool constructor = it.frame()->IsConstructor();
6026
6027 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006028 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006029 ScopeInfo<> info(*code);
6030
6031 // Get the context.
6032 Handle<Context> context(Context::cast(it.frame()->context()));
6033
6034 // Get the locals names and values into a temporary array.
6035 //
6036 // TODO(1240907): Hide compiler-introduced stack variables
6037 // (e.g. .result)? For users of the debugger, they will probably be
6038 // confusing.
6039 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6040 for (int i = 0; i < info.NumberOfLocals(); i++) {
6041 // Name of the local.
6042 locals->set(i * 2, *info.LocalName(i));
6043
6044 // Fetch the value of the local - either from the stack or from a
6045 // heap-allocated context.
6046 if (i < info.number_of_stack_slots()) {
6047 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6048 } else {
6049 Handle<String> name = info.LocalName(i);
6050 // Traverse the context chain to the function context as all local
6051 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006052 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006053 context = Handle<Context>(context->previous());
6054 }
6055 ASSERT(context->is_function_context());
6056 locals->set(i * 2 + 1,
6057 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6058 NULL)));
6059 }
6060 }
6061
6062 // Now advance to the arguments adapter frame (if any). If contains all
6063 // the provided parameters and
6064
6065 // Now advance to the arguments adapter frame (if any). It contains all
6066 // the provided parameters whereas the function frame always have the number
6067 // of arguments matching the functions parameters. The rest of the
6068 // information (except for what is collected above) is the same.
6069 it.AdvanceToArgumentsFrame();
6070
6071 // Find the number of arguments to fill. At least fill the number of
6072 // parameters for the function and fill more if more parameters are provided.
6073 int argument_count = info.number_of_parameters();
6074 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6075 argument_count = it.frame()->GetProvidedParametersCount();
6076 }
6077
6078 // Calculate the size of the result.
6079 int details_size = kFrameDetailsFirstDynamicIndex +
6080 2 * (argument_count + info.NumberOfLocals());
6081 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6082
6083 // Add the frame id.
6084 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6085
6086 // Add the function (same as in function frame).
6087 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6088
6089 // Add the arguments count.
6090 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6091
6092 // Add the locals count
6093 details->set(kFrameDetailsLocalCountIndex,
6094 Smi::FromInt(info.NumberOfLocals()));
6095
6096 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006097 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006098 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6099 } else {
6100 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6101 }
6102
6103 // Add the constructor information.
6104 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6105
6106 // Add information on whether this frame is invoked in the debugger context.
6107 details->set(kFrameDetailsDebuggerFrameIndex,
6108 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6109
6110 // Fill the dynamic part.
6111 int details_index = kFrameDetailsFirstDynamicIndex;
6112
6113 // Add arguments name and value.
6114 for (int i = 0; i < argument_count; i++) {
6115 // Name of the argument.
6116 if (i < info.number_of_parameters()) {
6117 details->set(details_index++, *info.parameter_name(i));
6118 } else {
6119 details->set(details_index++, Heap::undefined_value());
6120 }
6121
6122 // Parameter value.
6123 if (i < it.frame()->GetProvidedParametersCount()) {
6124 details->set(details_index++, it.frame()->GetParameter(i));
6125 } else {
6126 details->set(details_index++, Heap::undefined_value());
6127 }
6128 }
6129
6130 // Add locals name and value from the temporary copy from the function frame.
6131 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6132 details->set(details_index++, locals->get(i));
6133 }
6134
6135 // Add the receiver (same as in function frame).
6136 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6137 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6138 Handle<Object> receiver(it.frame()->receiver());
6139 if (!receiver->IsJSObject()) {
6140 // If the receiver is NOT a JSObject we have hit an optimization
6141 // where a value object is not converted into a wrapped JS objects.
6142 // To hide this optimization from the debugger, we wrap the receiver
6143 // by creating correct wrapper object based on the calling frame's
6144 // global context.
6145 it.Advance();
6146 Handle<Context> calling_frames_global_context(
6147 Context::cast(Context::cast(it.frame()->context())->global_context()));
6148 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6149 }
6150 details->set(kFrameDetailsReceiverIndex, *receiver);
6151
6152 ASSERT_EQ(details_size, details_index);
6153 return *Factory::NewJSArrayWithElements(details);
6154}
6155
6156
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006157// Copy all the context locals into an object used to materialize a scope.
6158static void CopyContextLocalsToScopeObject(Handle<Code> code,
6159 ScopeInfo<>& scope_info,
6160 Handle<Context> context,
6161 Handle<JSObject> scope_object) {
6162 // Fill all context locals to the context extension.
6163 for (int i = Context::MIN_CONTEXT_SLOTS;
6164 i < scope_info.number_of_context_slots();
6165 i++) {
6166 int context_index =
6167 ScopeInfo<>::ContextSlotIndex(*code,
6168 *scope_info.context_slot_name(i),
6169 NULL);
6170
6171 // Don't include the arguments shadow (.arguments) context variable.
6172 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6173 SetProperty(scope_object,
6174 scope_info.context_slot_name(i),
6175 Handle<Object>(context->get(context_index)), NONE);
6176 }
6177 }
6178}
6179
6180
6181// Create a plain JSObject which materializes the local scope for the specified
6182// frame.
6183static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6184 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6185 Handle<Code> code(function->code());
6186 ScopeInfo<> scope_info(*code);
6187
6188 // Allocate and initialize a JSObject with all the arguments, stack locals
6189 // heap locals and extension properties of the debugged function.
6190 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6191
6192 // First fill all parameters.
6193 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6194 SetProperty(local_scope,
6195 scope_info.parameter_name(i),
6196 Handle<Object>(frame->GetParameter(i)), NONE);
6197 }
6198
6199 // Second fill all stack locals.
6200 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6201 SetProperty(local_scope,
6202 scope_info.stack_slot_name(i),
6203 Handle<Object>(frame->GetExpression(i)), NONE);
6204 }
6205
6206 // Third fill all context locals.
6207 Handle<Context> frame_context(Context::cast(frame->context()));
6208 Handle<Context> function_context(frame_context->fcontext());
6209 CopyContextLocalsToScopeObject(code, scope_info,
6210 function_context, local_scope);
6211
6212 // Finally copy any properties from the function context extension. This will
6213 // be variables introduced by eval.
6214 if (function_context->closure() == *function) {
6215 if (function_context->has_extension() &&
6216 !function_context->IsGlobalContext()) {
6217 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6218 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6219 for (int i = 0; i < keys->length(); i++) {
6220 // Names of variables introduced by eval are strings.
6221 ASSERT(keys->get(i)->IsString());
6222 Handle<String> key(String::cast(keys->get(i)));
6223 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6224 }
6225 }
6226 }
6227 return local_scope;
6228}
6229
6230
6231// Create a plain JSObject which materializes the closure content for the
6232// context.
6233static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6234 ASSERT(context->is_function_context());
6235
6236 Handle<Code> code(context->closure()->code());
6237 ScopeInfo<> scope_info(*code);
6238
6239 // Allocate and initialize a JSObject with all the content of theis function
6240 // closure.
6241 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6242
6243 // Check whether the arguments shadow object exists.
6244 int arguments_shadow_index =
6245 ScopeInfo<>::ContextSlotIndex(*code,
6246 Heap::arguments_shadow_symbol(),
6247 NULL);
6248 if (arguments_shadow_index >= 0) {
6249 // In this case all the arguments are available in the arguments shadow
6250 // object.
6251 Handle<JSObject> arguments_shadow(
6252 JSObject::cast(context->get(arguments_shadow_index)));
6253 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6254 SetProperty(closure_scope,
6255 scope_info.parameter_name(i),
6256 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6257 }
6258 }
6259
6260 // Fill all context locals to the context extension.
6261 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6262
6263 // Finally copy any properties from the function context extension. This will
6264 // be variables introduced by eval.
6265 if (context->has_extension()) {
6266 Handle<JSObject> ext(JSObject::cast(context->extension()));
6267 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6268 for (int i = 0; i < keys->length(); i++) {
6269 // Names of variables introduced by eval are strings.
6270 ASSERT(keys->get(i)->IsString());
6271 Handle<String> key(String::cast(keys->get(i)));
6272 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6273 }
6274 }
6275
6276 return closure_scope;
6277}
6278
6279
6280// Iterate over the actual scopes visible from a stack frame. All scopes are
6281// backed by an actual context except the local scope, which is inserted
6282// "artifically" in the context chain.
6283class ScopeIterator {
6284 public:
6285 enum ScopeType {
6286 ScopeTypeGlobal = 0,
6287 ScopeTypeLocal,
6288 ScopeTypeWith,
6289 ScopeTypeClosure
6290 };
6291
6292 explicit ScopeIterator(JavaScriptFrame* frame)
6293 : frame_(frame),
6294 function_(JSFunction::cast(frame->function())),
6295 context_(Context::cast(frame->context())),
6296 local_done_(false),
6297 at_local_(false) {
6298
6299 // Check whether the first scope is actually a local scope.
6300 if (context_->IsGlobalContext()) {
6301 // If there is a stack slot for .result then this local scope has been
6302 // created for evaluating top level code and it is not a real local scope.
6303 // Checking for the existence of .result seems fragile, but the scope info
6304 // saved with the code object does not otherwise have that information.
6305 Handle<Code> code(function_->code());
6306 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6307 at_local_ = index < 0;
6308 } else if (context_->is_function_context()) {
6309 at_local_ = true;
6310 }
6311 }
6312
6313 // More scopes?
6314 bool Done() { return context_.is_null(); }
6315
6316 // Move to the next scope.
6317 void Next() {
6318 // If at a local scope mark the local scope as passed.
6319 if (at_local_) {
6320 at_local_ = false;
6321 local_done_ = true;
6322
6323 // If the current context is not associated with the local scope the
6324 // current context is the next real scope, so don't move to the next
6325 // context in this case.
6326 if (context_->closure() != *function_) {
6327 return;
6328 }
6329 }
6330
6331 // The global scope is always the last in the chain.
6332 if (context_->IsGlobalContext()) {
6333 context_ = Handle<Context>();
6334 return;
6335 }
6336
6337 // Move to the next context.
6338 if (context_->is_function_context()) {
6339 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6340 } else {
6341 context_ = Handle<Context>(context_->previous());
6342 }
6343
6344 // If passing the local scope indicate that the current scope is now the
6345 // local scope.
6346 if (!local_done_ &&
6347 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6348 at_local_ = true;
6349 }
6350 }
6351
6352 // Return the type of the current scope.
6353 int Type() {
6354 if (at_local_) {
6355 return ScopeTypeLocal;
6356 }
6357 if (context_->IsGlobalContext()) {
6358 ASSERT(context_->global()->IsGlobalObject());
6359 return ScopeTypeGlobal;
6360 }
6361 if (context_->is_function_context()) {
6362 return ScopeTypeClosure;
6363 }
6364 ASSERT(context_->has_extension());
6365 ASSERT(!context_->extension()->IsJSContextExtensionObject());
6366 return ScopeTypeWith;
6367 }
6368
6369 // Return the JavaScript object with the content of the current scope.
6370 Handle<JSObject> ScopeObject() {
6371 switch (Type()) {
6372 case ScopeIterator::ScopeTypeGlobal:
6373 return Handle<JSObject>(CurrentContext()->global());
6374 break;
6375 case ScopeIterator::ScopeTypeLocal:
6376 // Materialize the content of the local scope into a JSObject.
6377 return MaterializeLocalScope(frame_);
6378 break;
6379 case ScopeIterator::ScopeTypeWith:
6380 // Return the with object.
6381 return Handle<JSObject>(CurrentContext()->extension());
6382 break;
6383 case ScopeIterator::ScopeTypeClosure:
6384 // Materialize the content of the closure scope into a JSObject.
6385 return MaterializeClosure(CurrentContext());
6386 break;
6387 }
6388 UNREACHABLE();
6389 return Handle<JSObject>();
6390 }
6391
6392 // Return the context for this scope. For the local context there might not
6393 // be an actual context.
6394 Handle<Context> CurrentContext() {
6395 if (at_local_ && context_->closure() != *function_) {
6396 return Handle<Context>();
6397 }
6398 return context_;
6399 }
6400
6401#ifdef DEBUG
6402 // Debug print of the content of the current scope.
6403 void DebugPrint() {
6404 switch (Type()) {
6405 case ScopeIterator::ScopeTypeGlobal:
6406 PrintF("Global:\n");
6407 CurrentContext()->Print();
6408 break;
6409
6410 case ScopeIterator::ScopeTypeLocal: {
6411 PrintF("Local:\n");
6412 Handle<Code> code(function_->code());
6413 ScopeInfo<> scope_info(*code);
6414 scope_info.Print();
6415 if (!CurrentContext().is_null()) {
6416 CurrentContext()->Print();
6417 if (CurrentContext()->has_extension()) {
6418 Handle<JSObject> extension =
6419 Handle<JSObject>(CurrentContext()->extension());
6420 if (extension->IsJSContextExtensionObject()) {
6421 extension->Print();
6422 }
6423 }
6424 }
6425 break;
6426 }
6427
6428 case ScopeIterator::ScopeTypeWith: {
6429 PrintF("With:\n");
6430 Handle<JSObject> extension =
6431 Handle<JSObject>(CurrentContext()->extension());
6432 extension->Print();
6433 break;
6434 }
6435
6436 case ScopeIterator::ScopeTypeClosure: {
6437 PrintF("Closure:\n");
6438 CurrentContext()->Print();
6439 if (CurrentContext()->has_extension()) {
6440 Handle<JSObject> extension =
6441 Handle<JSObject>(CurrentContext()->extension());
6442 if (extension->IsJSContextExtensionObject()) {
6443 extension->Print();
6444 }
6445 }
6446 break;
6447 }
6448
6449 default:
6450 UNREACHABLE();
6451 }
6452 PrintF("\n");
6453 }
6454#endif
6455
6456 private:
6457 JavaScriptFrame* frame_;
6458 Handle<JSFunction> function_;
6459 Handle<Context> context_;
6460 bool local_done_;
6461 bool at_local_;
6462
6463 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6464};
6465
6466
6467static Object* Runtime_GetScopeCount(Arguments args) {
6468 HandleScope scope;
6469 ASSERT(args.length() == 2);
6470
6471 // Check arguments.
6472 Object* check = Runtime_CheckExecutionState(args);
6473 if (check->IsFailure()) return check;
6474 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6475
6476 // Get the frame where the debugging is performed.
6477 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6478 JavaScriptFrameIterator it(id);
6479 JavaScriptFrame* frame = it.frame();
6480
6481 // Count the visible scopes.
6482 int n = 0;
6483 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6484 n++;
6485 }
6486
6487 return Smi::FromInt(n);
6488}
6489
6490
6491static const int kScopeDetailsTypeIndex = 0;
6492static const int kScopeDetailsObjectIndex = 1;
6493static const int kScopeDetailsSize = 2;
6494
6495// Return an array with scope details
6496// args[0]: number: break id
6497// args[1]: number: frame index
6498// args[2]: number: scope index
6499//
6500// The array returned contains the following information:
6501// 0: Scope type
6502// 1: Scope object
6503static Object* Runtime_GetScopeDetails(Arguments args) {
6504 HandleScope scope;
6505 ASSERT(args.length() == 3);
6506
6507 // Check arguments.
6508 Object* check = Runtime_CheckExecutionState(args);
6509 if (check->IsFailure()) return check;
6510 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6511 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6512
6513 // Get the frame where the debugging is performed.
6514 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6515 JavaScriptFrameIterator frame_it(id);
6516 JavaScriptFrame* frame = frame_it.frame();
6517
6518 // Find the requested scope.
6519 int n = 0;
6520 ScopeIterator it(frame);
6521 for (; !it.Done() && n < index; it.Next()) {
6522 n++;
6523 }
6524 if (it.Done()) {
6525 return Heap::undefined_value();
6526 }
6527
6528 // Calculate the size of the result.
6529 int details_size = kScopeDetailsSize;
6530 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6531
6532 // Fill in scope details.
6533 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6534 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6535
6536 return *Factory::NewJSArrayWithElements(details);
6537}
6538
6539
6540static Object* Runtime_DebugPrintScopes(Arguments args) {
6541 HandleScope scope;
6542 ASSERT(args.length() == 0);
6543
6544#ifdef DEBUG
6545 // Print the scopes for the top frame.
6546 StackFrameLocator locator;
6547 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6548 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6549 it.DebugPrint();
6550 }
6551#endif
6552 return Heap::undefined_value();
6553}
6554
6555
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006556static Object* Runtime_GetCFrames(Arguments args) {
6557 HandleScope scope;
6558 ASSERT(args.length() == 1);
6559 Object* result = Runtime_CheckExecutionState(args);
6560 if (result->IsFailure()) return result;
6561
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006562#if V8_HOST_ARCH_64_BIT
6563 UNIMPLEMENTED();
6564 return Heap::undefined_value();
6565#else
6566
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006567 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006568 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6569 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006570 if (frames_count == OS::kStackWalkError) {
6571 return Heap::undefined_value();
6572 }
6573
6574 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6575 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6576 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6577 for (int i = 0; i < frames_count; i++) {
6578 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6579 frame_value->SetProperty(
6580 *address_str,
6581 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6582 NONE);
6583
6584 // Get the stack walk text for this frame.
6585 Handle<String> frame_text;
6586 if (strlen(frames[i].text) > 0) {
6587 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6588 frame_text = Factory::NewStringFromAscii(str);
6589 }
6590
6591 if (!frame_text.is_null()) {
6592 frame_value->SetProperty(*text_str, *frame_text, NONE);
6593 }
6594
6595 frames_array->set(i, *frame_value);
6596 }
6597 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006598#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006599}
6600
6601
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006602static Object* Runtime_GetThreadCount(Arguments args) {
6603 HandleScope scope;
6604 ASSERT(args.length() == 1);
6605
6606 // Check arguments.
6607 Object* result = Runtime_CheckExecutionState(args);
6608 if (result->IsFailure()) return result;
6609
6610 // Count all archived V8 threads.
6611 int n = 0;
6612 for (ThreadState* thread = ThreadState::FirstInUse();
6613 thread != NULL;
6614 thread = thread->Next()) {
6615 n++;
6616 }
6617
6618 // Total number of threads is current thread and archived threads.
6619 return Smi::FromInt(n + 1);
6620}
6621
6622
6623static const int kThreadDetailsCurrentThreadIndex = 0;
6624static const int kThreadDetailsThreadIdIndex = 1;
6625static const int kThreadDetailsSize = 2;
6626
6627// Return an array with thread details
6628// args[0]: number: break id
6629// args[1]: number: thread index
6630//
6631// The array returned contains the following information:
6632// 0: Is current thread?
6633// 1: Thread id
6634static Object* Runtime_GetThreadDetails(Arguments args) {
6635 HandleScope scope;
6636 ASSERT(args.length() == 2);
6637
6638 // Check arguments.
6639 Object* check = Runtime_CheckExecutionState(args);
6640 if (check->IsFailure()) return check;
6641 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6642
6643 // Allocate array for result.
6644 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6645
6646 // Thread index 0 is current thread.
6647 if (index == 0) {
6648 // Fill the details.
6649 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6650 details->set(kThreadDetailsThreadIdIndex,
6651 Smi::FromInt(ThreadManager::CurrentId()));
6652 } else {
6653 // Find the thread with the requested index.
6654 int n = 1;
6655 ThreadState* thread = ThreadState::FirstInUse();
6656 while (index != n && thread != NULL) {
6657 thread = thread->Next();
6658 n++;
6659 }
6660 if (thread == NULL) {
6661 return Heap::undefined_value();
6662 }
6663
6664 // Fill the details.
6665 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6666 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6667 }
6668
6669 // Convert to JS array and return.
6670 return *Factory::NewJSArrayWithElements(details);
6671}
6672
6673
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006674static Object* Runtime_GetBreakLocations(Arguments args) {
6675 HandleScope scope;
6676 ASSERT(args.length() == 1);
6677
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006678 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6679 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006680 // Find the number of break points
6681 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6682 if (break_locations->IsUndefined()) return Heap::undefined_value();
6683 // Return array as JS array
6684 return *Factory::NewJSArrayWithElements(
6685 Handle<FixedArray>::cast(break_locations));
6686}
6687
6688
6689// Set a break point in a function
6690// args[0]: function
6691// args[1]: number: break source position (within the function source)
6692// args[2]: number: break point object
6693static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6694 HandleScope scope;
6695 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006696 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6697 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006698 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6699 RUNTIME_ASSERT(source_position >= 0);
6700 Handle<Object> break_point_object_arg = args.at<Object>(2);
6701
6702 // Set break point.
6703 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6704
6705 return Heap::undefined_value();
6706}
6707
6708
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006709Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6710 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006711 // Iterate the heap looking for SharedFunctionInfo generated from the
6712 // script. The inner most SharedFunctionInfo containing the source position
6713 // for the requested break point is found.
6714 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6715 // which is found is not compiled it is compiled and the heap is iterated
6716 // again as the compilation might create inner functions from the newly
6717 // compiled function and the actual requested break point might be in one of
6718 // these functions.
6719 bool done = false;
6720 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006721 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006722 Handle<SharedFunctionInfo> target;
6723 // The current candidate for the last function in script:
6724 Handle<SharedFunctionInfo> last;
6725 while (!done) {
6726 HeapIterator iterator;
6727 while (iterator.has_next()) {
6728 HeapObject* obj = iterator.next();
6729 ASSERT(obj != NULL);
6730 if (obj->IsSharedFunctionInfo()) {
6731 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6732 if (shared->script() == *script) {
6733 // If the SharedFunctionInfo found has the requested script data and
6734 // contains the source position it is a candidate.
6735 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006736 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006737 start_position = shared->start_position();
6738 }
6739 if (start_position <= position &&
6740 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006741 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006742 // candidate this is the new candidate.
6743 if (target.is_null()) {
6744 target_start_position = start_position;
6745 target = shared;
6746 } else {
6747 if (target_start_position < start_position &&
6748 shared->end_position() < target->end_position()) {
6749 target_start_position = start_position;
6750 target = shared;
6751 }
6752 }
6753 }
6754
6755 // Keep track of the last function in the script.
6756 if (last.is_null() ||
6757 shared->end_position() > last->start_position()) {
6758 last = shared;
6759 }
6760 }
6761 }
6762 }
6763
6764 // Make sure some candidate is selected.
6765 if (target.is_null()) {
6766 if (!last.is_null()) {
6767 // Position after the last function - use last.
6768 target = last;
6769 } else {
6770 // Unable to find function - possibly script without any function.
6771 return Heap::undefined_value();
6772 }
6773 }
6774
6775 // If the candidate found is compiled we are done. NOTE: when lazy
6776 // compilation of inner functions is introduced some additional checking
6777 // needs to be done here to compile inner functions.
6778 done = target->is_compiled();
6779 if (!done) {
6780 // If the candidate is not compiled compile it to reveal any inner
6781 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006782 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006783 }
6784 }
6785
6786 return *target;
6787}
6788
6789
6790// Change the state of a break point in a script. NOTE: Regarding performance
6791// see the NOTE for GetScriptFromScriptData.
6792// args[0]: script to set break point in
6793// args[1]: number: break source position (within the script source)
6794// args[2]: number: break point object
6795static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6796 HandleScope scope;
6797 ASSERT(args.length() == 3);
6798 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6799 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6800 RUNTIME_ASSERT(source_position >= 0);
6801 Handle<Object> break_point_object_arg = args.at<Object>(2);
6802
6803 // Get the script from the script wrapper.
6804 RUNTIME_ASSERT(wrapper->value()->IsScript());
6805 Handle<Script> script(Script::cast(wrapper->value()));
6806
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006807 Object* result = Runtime::FindSharedFunctionInfoInScript(
6808 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006809 if (!result->IsUndefined()) {
6810 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6811 // Find position within function. The script position might be before the
6812 // source position of the first function.
6813 int position;
6814 if (shared->start_position() > source_position) {
6815 position = 0;
6816 } else {
6817 position = source_position - shared->start_position();
6818 }
6819 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6820 }
6821 return Heap::undefined_value();
6822}
6823
6824
6825// Clear a break point
6826// args[0]: number: break point object
6827static Object* Runtime_ClearBreakPoint(Arguments args) {
6828 HandleScope scope;
6829 ASSERT(args.length() == 1);
6830 Handle<Object> break_point_object_arg = args.at<Object>(0);
6831
6832 // Clear break point.
6833 Debug::ClearBreakPoint(break_point_object_arg);
6834
6835 return Heap::undefined_value();
6836}
6837
6838
6839// Change the state of break on exceptions
6840// args[0]: boolean indicating uncaught exceptions
6841// args[1]: boolean indicating on/off
6842static Object* Runtime_ChangeBreakOnException(Arguments args) {
6843 HandleScope scope;
6844 ASSERT(args.length() == 2);
6845 ASSERT(args[0]->IsNumber());
6846 ASSERT(args[1]->IsBoolean());
6847
6848 // Update break point state
6849 ExceptionBreakType type =
6850 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6851 bool enable = args[1]->ToBoolean()->IsTrue();
6852 Debug::ChangeBreakOnException(type, enable);
6853 return Heap::undefined_value();
6854}
6855
6856
6857// Prepare for stepping
6858// args[0]: break id for checking execution state
6859// args[1]: step action from the enumeration StepAction
6860// args[2]: number of times to perform the step
6861static Object* Runtime_PrepareStep(Arguments args) {
6862 HandleScope scope;
6863 ASSERT(args.length() == 3);
6864 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006865 Object* check = Runtime_CheckExecutionState(args);
6866 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006867 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6868 return Top::Throw(Heap::illegal_argument_symbol());
6869 }
6870
6871 // Get the step action and check validity.
6872 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6873 if (step_action != StepIn &&
6874 step_action != StepNext &&
6875 step_action != StepOut &&
6876 step_action != StepInMin &&
6877 step_action != StepMin) {
6878 return Top::Throw(Heap::illegal_argument_symbol());
6879 }
6880
6881 // Get the number of steps.
6882 int step_count = NumberToInt32(args[2]);
6883 if (step_count < 1) {
6884 return Top::Throw(Heap::illegal_argument_symbol());
6885 }
6886
6887 // Prepare step.
6888 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6889 return Heap::undefined_value();
6890}
6891
6892
6893// Clear all stepping set by PrepareStep.
6894static Object* Runtime_ClearStepping(Arguments args) {
6895 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006896 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006897 Debug::ClearStepping();
6898 return Heap::undefined_value();
6899}
6900
6901
6902// Creates a copy of the with context chain. The copy of the context chain is
6903// is linked to the function context supplied.
6904static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6905 Handle<Context> function_context) {
6906 // At the bottom of the chain. Return the function context to link to.
6907 if (context_chain->is_function_context()) {
6908 return function_context;
6909 }
6910
6911 // Recursively copy the with contexts.
6912 Handle<Context> previous(context_chain->previous());
6913 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6914 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006915 CopyWithContextChain(function_context, previous),
6916 extension,
6917 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006918}
6919
6920
6921// Helper function to find or create the arguments object for
6922// Runtime_DebugEvaluate.
6923static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6924 Handle<JSFunction> function,
6925 Handle<Code> code,
6926 const ScopeInfo<>* sinfo,
6927 Handle<Context> function_context) {
6928 // Try to find the value of 'arguments' to pass as parameter. If it is not
6929 // found (that is the debugged function does not reference 'arguments' and
6930 // does not support eval) then create an 'arguments' object.
6931 int index;
6932 if (sinfo->number_of_stack_slots() > 0) {
6933 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6934 if (index != -1) {
6935 return Handle<Object>(frame->GetExpression(index));
6936 }
6937 }
6938
6939 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6940 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6941 NULL);
6942 if (index != -1) {
6943 return Handle<Object>(function_context->get(index));
6944 }
6945 }
6946
6947 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006948 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6949 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006950 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006951 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006952 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006953 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006954 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006955 return arguments;
6956}
6957
6958
6959// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006960// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006961// extension part has all the parameters and locals of the function on the
6962// stack frame. A function which calls eval with the code to evaluate is then
6963// compiled in this context and called in this context. As this context
6964// replaces the context of the function on the stack frame a new (empty)
6965// function is created as well to be used as the closure for the context.
6966// This function and the context acts as replacements for the function on the
6967// stack frame presenting the same view of the values of parameters and
6968// local variables as if the piece of JavaScript was evaluated at the point
6969// where the function on the stack frame is currently stopped.
6970static Object* Runtime_DebugEvaluate(Arguments args) {
6971 HandleScope scope;
6972
6973 // Check the execution state and decode arguments frame and source to be
6974 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006975 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006976 Object* check_result = Runtime_CheckExecutionState(args);
6977 if (check_result->IsFailure()) return check_result;
6978 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6979 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006980 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6981
6982 // Handle the processing of break.
6983 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006984
6985 // Get the frame where the debugging is performed.
6986 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6987 JavaScriptFrameIterator it(id);
6988 JavaScriptFrame* frame = it.frame();
6989 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6990 Handle<Code> code(function->code());
6991 ScopeInfo<> sinfo(*code);
6992
6993 // Traverse the saved contexts chain to find the active context for the
6994 // selected frame.
6995 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006996 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006997 save = save->prev();
6998 }
6999 ASSERT(save != NULL);
7000 SaveContext savex;
7001 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007002
7003 // Create the (empty) function replacing the function on the stack frame for
7004 // the purpose of evaluating in the context created below. It is important
7005 // that this function does not describe any parameters and local variables
7006 // in the context. If it does then this will cause problems with the lookup
7007 // in Context::Lookup, where context slots for parameters and local variables
7008 // are looked at before the extension object.
7009 Handle<JSFunction> go_between =
7010 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7011 go_between->set_context(function->context());
7012#ifdef DEBUG
7013 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7014 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7015 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7016#endif
7017
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007018 // Materialize the content of the local scope into a JSObject.
7019 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007020
7021 // Allocate a new context for the debug evaluation and set the extension
7022 // object build.
7023 Handle<Context> context =
7024 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007025 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007026 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007027 Handle<Context> frame_context(Context::cast(frame->context()));
7028 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007029 context = CopyWithContextChain(frame_context, context);
7030
7031 // Wrap the evaluation statement in a new function compiled in the newly
7032 // created context. The function has one parameter which has to be called
7033 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007034 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007035 // function(arguments,__source__) {return eval(__source__);}
7036 static const char* source_str =
7037 "function(arguments,__source__){return eval(__source__);}";
7038 static const int source_str_length = strlen(source_str);
7039 Handle<String> function_source =
7040 Factory::NewStringFromAscii(Vector<const char>(source_str,
7041 source_str_length));
7042 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007043 Compiler::CompileEval(function_source,
7044 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007045 context->IsGlobalContext(),
7046 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007047 if (boilerplate.is_null()) return Failure::Exception();
7048 Handle<JSFunction> compiled_function =
7049 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7050
7051 // Invoke the result of the compilation to get the evaluation function.
7052 bool has_pending_exception;
7053 Handle<Object> receiver(frame->receiver());
7054 Handle<Object> evaluation_function =
7055 Execution::Call(compiled_function, receiver, 0, NULL,
7056 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007057 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007058
7059 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7060 function_context);
7061
7062 // Invoke the evaluation function and return the result.
7063 const int argc = 2;
7064 Object** argv[argc] = { arguments.location(),
7065 Handle<Object>::cast(source).location() };
7066 Handle<Object> result =
7067 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7068 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007069 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007070
7071 // Skip the global proxy as it has no properties and always delegates to the
7072 // real global object.
7073 if (result->IsJSGlobalProxy()) {
7074 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7075 }
7076
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007077 return *result;
7078}
7079
7080
7081static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7082 HandleScope scope;
7083
7084 // Check the execution state and decode arguments frame and source to be
7085 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007086 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007087 Object* check_result = Runtime_CheckExecutionState(args);
7088 if (check_result->IsFailure()) return check_result;
7089 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007090 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7091
7092 // Handle the processing of break.
7093 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007094
7095 // Enter the top context from before the debugger was invoked.
7096 SaveContext save;
7097 SaveContext* top = &save;
7098 while (top != NULL && *top->context() == *Debug::debug_context()) {
7099 top = top->prev();
7100 }
7101 if (top != NULL) {
7102 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007103 }
7104
7105 // Get the global context now set to the top context from before the
7106 // debugger was invoked.
7107 Handle<Context> context = Top::global_context();
7108
7109 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007110 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007111 Handle<JSFunction>(Compiler::CompileEval(source,
7112 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007113 true,
7114 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007115 if (boilerplate.is_null()) return Failure::Exception();
7116 Handle<JSFunction> compiled_function =
7117 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7118 context));
7119
7120 // Invoke the result of the compilation to get the evaluation function.
7121 bool has_pending_exception;
7122 Handle<Object> receiver = Top::global();
7123 Handle<Object> result =
7124 Execution::Call(compiled_function, receiver, 0, NULL,
7125 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007126 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127 return *result;
7128}
7129
7130
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007131static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7132 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007133 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007134
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007135 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007136 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007137
7138 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007139 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007140 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7141 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7142 // because using
7143 // instances->set(i, *GetScriptWrapper(script))
7144 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7145 // already have deferenced the instances handle.
7146 Handle<JSValue> wrapper = GetScriptWrapper(script);
7147 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007148 }
7149
7150 // Return result as a JS array.
7151 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7152 Handle<JSArray>::cast(result)->SetContent(*instances);
7153 return *result;
7154}
7155
7156
7157// Helper function used by Runtime_DebugReferencedBy below.
7158static int DebugReferencedBy(JSObject* target,
7159 Object* instance_filter, int max_references,
7160 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007161 JSFunction* arguments_function) {
7162 NoHandleAllocation ha;
7163 AssertNoAllocation no_alloc;
7164
7165 // Iterate the heap.
7166 int count = 0;
7167 JSObject* last = NULL;
7168 HeapIterator iterator;
7169 while (iterator.has_next() &&
7170 (max_references == 0 || count < max_references)) {
7171 // Only look at all JSObjects.
7172 HeapObject* heap_obj = iterator.next();
7173 if (heap_obj->IsJSObject()) {
7174 // Skip context extension objects and argument arrays as these are
7175 // checked in the context of functions using them.
7176 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007177 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007178 obj->map()->constructor() == arguments_function) {
7179 continue;
7180 }
7181
7182 // Check if the JS object has a reference to the object looked for.
7183 if (obj->ReferencesObject(target)) {
7184 // Check instance filter if supplied. This is normally used to avoid
7185 // references from mirror objects (see Runtime_IsInPrototypeChain).
7186 if (!instance_filter->IsUndefined()) {
7187 Object* V = obj;
7188 while (true) {
7189 Object* prototype = V->GetPrototype();
7190 if (prototype->IsNull()) {
7191 break;
7192 }
7193 if (instance_filter == prototype) {
7194 obj = NULL; // Don't add this object.
7195 break;
7196 }
7197 V = prototype;
7198 }
7199 }
7200
7201 if (obj != NULL) {
7202 // Valid reference found add to instance array if supplied an update
7203 // count.
7204 if (instances != NULL && count < instances_size) {
7205 instances->set(count, obj);
7206 }
7207 last = obj;
7208 count++;
7209 }
7210 }
7211 }
7212 }
7213
7214 // Check for circular reference only. This can happen when the object is only
7215 // referenced from mirrors and has a circular reference in which case the
7216 // object is not really alive and would have been garbage collected if not
7217 // referenced from the mirror.
7218 if (count == 1 && last == target) {
7219 count = 0;
7220 }
7221
7222 // Return the number of referencing objects found.
7223 return count;
7224}
7225
7226
7227// Scan the heap for objects with direct references to an object
7228// args[0]: the object to find references to
7229// args[1]: constructor function for instances to exclude (Mirror)
7230// args[2]: the the maximum number of objects to return
7231static Object* Runtime_DebugReferencedBy(Arguments args) {
7232 ASSERT(args.length() == 3);
7233
7234 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007235 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007236
7237 // Check parameters.
7238 CONVERT_CHECKED(JSObject, target, args[0]);
7239 Object* instance_filter = args[1];
7240 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7241 instance_filter->IsJSObject());
7242 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7243 RUNTIME_ASSERT(max_references >= 0);
7244
7245 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007246 JSObject* arguments_boilerplate =
7247 Top::context()->global_context()->arguments_boilerplate();
7248 JSFunction* arguments_function =
7249 JSFunction::cast(arguments_boilerplate->map()->constructor());
7250
7251 // Get the number of referencing objects.
7252 int count;
7253 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007254 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007255
7256 // Allocate an array to hold the result.
7257 Object* object = Heap::AllocateFixedArray(count);
7258 if (object->IsFailure()) return object;
7259 FixedArray* instances = FixedArray::cast(object);
7260
7261 // Fill the referencing objects.
7262 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007263 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007264
7265 // Return result as JS array.
7266 Object* result =
7267 Heap::AllocateJSObject(
7268 Top::context()->global_context()->array_function());
7269 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7270 return result;
7271}
7272
7273
7274// Helper function used by Runtime_DebugConstructedBy below.
7275static int DebugConstructedBy(JSFunction* constructor, int max_references,
7276 FixedArray* instances, int instances_size) {
7277 AssertNoAllocation no_alloc;
7278
7279 // Iterate the heap.
7280 int count = 0;
7281 HeapIterator iterator;
7282 while (iterator.has_next() &&
7283 (max_references == 0 || count < max_references)) {
7284 // Only look at all JSObjects.
7285 HeapObject* heap_obj = iterator.next();
7286 if (heap_obj->IsJSObject()) {
7287 JSObject* obj = JSObject::cast(heap_obj);
7288 if (obj->map()->constructor() == constructor) {
7289 // Valid reference found add to instance array if supplied an update
7290 // count.
7291 if (instances != NULL && count < instances_size) {
7292 instances->set(count, obj);
7293 }
7294 count++;
7295 }
7296 }
7297 }
7298
7299 // Return the number of referencing objects found.
7300 return count;
7301}
7302
7303
7304// Scan the heap for objects constructed by a specific function.
7305// args[0]: the constructor to find instances of
7306// args[1]: the the maximum number of objects to return
7307static Object* Runtime_DebugConstructedBy(Arguments args) {
7308 ASSERT(args.length() == 2);
7309
7310 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007311 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007312
7313 // Check parameters.
7314 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7315 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7316 RUNTIME_ASSERT(max_references >= 0);
7317
7318 // Get the number of referencing objects.
7319 int count;
7320 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7321
7322 // Allocate an array to hold the result.
7323 Object* object = Heap::AllocateFixedArray(count);
7324 if (object->IsFailure()) return object;
7325 FixedArray* instances = FixedArray::cast(object);
7326
7327 // Fill the referencing objects.
7328 count = DebugConstructedBy(constructor, max_references, instances, count);
7329
7330 // Return result as JS array.
7331 Object* result =
7332 Heap::AllocateJSObject(
7333 Top::context()->global_context()->array_function());
7334 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7335 return result;
7336}
7337
7338
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007339// Find the effective prototype object as returned by __proto__.
7340// args[0]: the object to find the prototype for.
7341static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007342 ASSERT(args.length() == 1);
7343
7344 CONVERT_CHECKED(JSObject, obj, args[0]);
7345
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007346 // Use the __proto__ accessor.
7347 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007348}
7349
7350
7351static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007352 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007353 CPU::DebugBreak();
7354 return Heap::undefined_value();
7355}
7356
7357
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007358static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
7359#ifdef DEBUG
7360 HandleScope scope;
7361 ASSERT(args.length() == 1);
7362 // Get the function and make sure it is compiled.
7363 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7364 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7365 return Failure::Exception();
7366 }
7367 func->code()->PrintLn();
7368#endif // DEBUG
7369 return Heap::undefined_value();
7370}
ager@chromium.org9085a012009-05-11 19:22:57 +00007371
7372
7373static Object* Runtime_FunctionGetInferredName(Arguments args) {
7374 NoHandleAllocation ha;
7375 ASSERT(args.length() == 1);
7376
7377 CONVERT_CHECKED(JSFunction, f, args[0]);
7378 return f->shared()->inferred_name();
7379}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007380#endif // ENABLE_DEBUGGER_SUPPORT
7381
7382
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007383// Finds the script object from the script data. NOTE: This operation uses
7384// heap traversal to find the function generated for the source position
7385// for the requested break point. For lazily compiled functions several heap
7386// traversals might be required rendering this operation as a rather slow
7387// operation. However for setting break points which is normally done through
7388// some kind of user interaction the performance is not crucial.
7389static Handle<Object> Runtime_GetScriptFromScriptName(
7390 Handle<String> script_name) {
7391 // Scan the heap for Script objects to find the script with the requested
7392 // script data.
7393 Handle<Script> script;
7394 HeapIterator iterator;
7395 while (script.is_null() && iterator.has_next()) {
7396 HeapObject* obj = iterator.next();
7397 // If a script is found check if it has the script data requested.
7398 if (obj->IsScript()) {
7399 if (Script::cast(obj)->name()->IsString()) {
7400 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7401 script = Handle<Script>(Script::cast(obj));
7402 }
7403 }
7404 }
7405 }
7406
7407 // If no script with the requested script data is found return undefined.
7408 if (script.is_null()) return Factory::undefined_value();
7409
7410 // Return the script found.
7411 return GetScriptWrapper(script);
7412}
7413
7414
7415// Get the script object from script data. NOTE: Regarding performance
7416// see the NOTE for GetScriptFromScriptData.
7417// args[0]: script data for the script to find the source for
7418static Object* Runtime_GetScript(Arguments args) {
7419 HandleScope scope;
7420
7421 ASSERT(args.length() == 1);
7422
7423 CONVERT_CHECKED(String, script_name, args[0]);
7424
7425 // Find the requested script.
7426 Handle<Object> result =
7427 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7428 return *result;
7429}
7430
7431
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007432// Determines whether the given stack frame should be displayed in
7433// a stack trace. The caller is the error constructor that asked
7434// for the stack trace to be collected. The first time a construct
7435// call to this function is encountered it is skipped. The seen_caller
7436// in/out parameter is used to remember if the caller has been seen
7437// yet.
7438static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7439 bool* seen_caller) {
7440 // Only display JS frames.
7441 if (!raw_frame->is_java_script())
7442 return false;
7443 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7444 Object* raw_fun = frame->function();
7445 // Not sure when this can happen but skip it just in case.
7446 if (!raw_fun->IsJSFunction())
7447 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007448 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007449 *seen_caller = true;
7450 return false;
7451 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007452 // Skip all frames until we've seen the caller. Also, skip the most
7453 // obvious builtin calls. Some builtin calls (such as Number.ADD
7454 // which is invoked using 'call') are very difficult to recognize
7455 // so we're leaving them in for now.
7456 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007457}
7458
7459
7460// Collect the raw data for a stack trace. Returns an array of three
7461// element segments each containing a receiver, function and native
7462// code offset.
7463static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007464 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007465 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007466 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7467
7468 HandleScope scope;
7469
7470 int initial_size = limit < 10 ? limit : 10;
7471 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007472
7473 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007474 // If the caller parameter is a function we skip frames until we're
7475 // under it before starting to collect.
7476 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007477 int cursor = 0;
7478 int frames_seen = 0;
7479 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007480 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007481 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007482 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007483 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007484 Object* recv = frame->receiver();
7485 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007486 Address pc = frame->pc();
7487 Address start = frame->code()->address();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007488 Smi* offset = Smi::FromInt(pc - start);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007489 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007490 if (cursor + 2 < elements->length()) {
7491 elements->set(cursor++, recv);
7492 elements->set(cursor++, fun);
7493 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7494 } else {
7495 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007496 Handle<Object> recv_handle(recv);
7497 Handle<Object> fun_handle(fun);
7498 SetElement(result, cursor++, recv_handle);
7499 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007500 SetElement(result, cursor++, Handle<Smi>(offset));
7501 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007502 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007503 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007504 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007505
7506 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7507
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007508 return *result;
7509}
7510
7511
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007512static Object* Runtime_Abort(Arguments args) {
7513 ASSERT(args.length() == 2);
7514 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7515 Smi::cast(args[1])->value());
7516 Top::PrintStack();
7517 OS::Abort();
7518 UNREACHABLE();
7519 return NULL;
7520}
7521
7522
kasper.lund44510672008-07-25 07:37:58 +00007523#ifdef DEBUG
7524// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7525// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007526static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007527 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007528 HandleScope scope;
7529 Handle<JSArray> result = Factory::NewJSArray(0);
7530 int index = 0;
7531#define ADD_ENTRY(Name, argc) \
7532 { \
7533 HandleScope inner; \
7534 Handle<String> name = \
7535 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7536 Handle<JSArray> pair = Factory::NewJSArray(0); \
7537 SetElement(pair, 0, name); \
7538 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7539 SetElement(result, index++, pair); \
7540 }
7541 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7542#undef ADD_ENTRY
7543 return *result;
7544}
kasper.lund44510672008-07-25 07:37:58 +00007545#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007546
7547
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007548static Object* Runtime_Log(Arguments args) {
7549 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007550 CONVERT_CHECKED(String, format, args[0]);
7551 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007552 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007553 Logger::LogRuntime(chars, elms);
7554 return Heap::undefined_value();
7555}
7556
7557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007558static Object* Runtime_IS_VAR(Arguments args) {
7559 UNREACHABLE(); // implemented as macro in the parser
7560 return NULL;
7561}
7562
7563
7564// ----------------------------------------------------------------------------
7565// Implementation of Runtime
7566
7567#define F(name, nargs) \
7568 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7569 static_cast<int>(Runtime::k##name) },
7570
7571static Runtime::Function Runtime_functions[] = {
7572 RUNTIME_FUNCTION_LIST(F)
7573 { NULL, NULL, NULL, 0, -1 }
7574};
7575
7576#undef F
7577
7578
7579Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7580 ASSERT(0 <= fid && fid < kNofFunctions);
7581 return &Runtime_functions[fid];
7582}
7583
7584
7585Runtime::Function* Runtime::FunctionForName(const char* name) {
7586 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7587 if (strcmp(f->name, name) == 0) {
7588 return f;
7589 }
7590 }
7591 return NULL;
7592}
7593
7594
7595void Runtime::PerformGC(Object* result) {
7596 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007597 if (failure->IsRetryAfterGC()) {
7598 // Try to do a garbage collection; ignore it if it fails. The C
7599 // entry stub will throw an out-of-memory exception in that case.
7600 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7601 } else {
7602 // Handle last resort GC and make sure to allow future allocations
7603 // to grow the heap without causing GCs (if possible).
7604 Counters::gc_last_resort_from_js.Increment();
7605 Heap::CollectAllGarbage();
7606 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007607}
7608
7609
7610} } // namespace v8::internal