blob: 845ac6308c4cc97dcab910d98aa87fbd03ad4301 [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
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001025static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1026 Arguments args) {
1027 HandleScope scope;
1028 ASSERT(args.length() == 2);
1029 CONVERT_ARG_CHECKED(JSObject, object, 0);
1030 CONVERT_SMI_CHECKED(properties, args[1]);
1031 if (object->HasFastProperties()) {
1032 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1033 }
1034 return *object;
1035}
1036
1037
1038static Object* Runtime_TransformToFastProperties(Arguments args) {
1039 HandleScope scope;
1040 ASSERT(args.length() == 1);
1041 CONVERT_ARG_CHECKED(JSObject, object, 0);
1042 if (!object->HasFastProperties() && !object->IsGlobalObject()) {
1043 TransformToFastProperties(object, 0);
1044 }
1045 return *object;
1046}
1047
1048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001049static Object* Runtime_RegExpExec(Arguments args) {
1050 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001051 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001052 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1053 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001054 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001055 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001056 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001057 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001058 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001059 RUNTIME_ASSERT(index >= 0);
1060 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001061 Handle<Object> result = RegExpImpl::Exec(regexp,
1062 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001063 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001064 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001065 if (result.is_null()) return Failure::Exception();
1066 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001067}
1068
1069
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1071 HandleScope scope;
1072 ASSERT(args.length() == 4);
1073 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1074 int index = Smi::cast(args[1])->value();
1075 Handle<String> pattern = args.at<String>(2);
1076 Handle<String> flags = args.at<String>(3);
1077
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001078 // Get the RegExp function from the context in the literals array.
1079 // This is the RegExp function from the context in which the
1080 // function was created. We do not use the RegExp function from the
1081 // current global context because this might be the RegExp function
1082 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001083 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001084 Handle<JSFunction>(
1085 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001086 // Compute the regular expression literal.
1087 bool has_pending_exception;
1088 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001089 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1090 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001091 if (has_pending_exception) {
1092 ASSERT(Top::has_pending_exception());
1093 return Failure::Exception();
1094 }
1095 literals->set(index, *regexp);
1096 return *regexp;
1097}
1098
1099
1100static Object* Runtime_FunctionGetName(Arguments args) {
1101 NoHandleAllocation ha;
1102 ASSERT(args.length() == 1);
1103
1104 CONVERT_CHECKED(JSFunction, f, args[0]);
1105 return f->shared()->name();
1106}
1107
1108
ager@chromium.org236ad962008-09-25 09:45:57 +00001109static Object* Runtime_FunctionSetName(Arguments args) {
1110 NoHandleAllocation ha;
1111 ASSERT(args.length() == 2);
1112
1113 CONVERT_CHECKED(JSFunction, f, args[0]);
1114 CONVERT_CHECKED(String, name, args[1]);
1115 f->shared()->set_name(name);
1116 return Heap::undefined_value();
1117}
1118
1119
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001120static Object* Runtime_FunctionGetScript(Arguments args) {
1121 HandleScope scope;
1122 ASSERT(args.length() == 1);
1123
1124 CONVERT_CHECKED(JSFunction, fun, args[0]);
1125 Handle<Object> script = Handle<Object>(fun->shared()->script());
1126 if (!script->IsScript()) return Heap::undefined_value();
1127
1128 return *GetScriptWrapper(Handle<Script>::cast(script));
1129}
1130
1131
1132static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1133 NoHandleAllocation ha;
1134 ASSERT(args.length() == 1);
1135
1136 CONVERT_CHECKED(JSFunction, f, args[0]);
1137 return f->shared()->GetSourceCode();
1138}
1139
1140
1141static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1142 NoHandleAllocation ha;
1143 ASSERT(args.length() == 1);
1144
1145 CONVERT_CHECKED(JSFunction, fun, args[0]);
1146 int pos = fun->shared()->start_position();
1147 return Smi::FromInt(pos);
1148}
1149
1150
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001151static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1152 ASSERT(args.length() == 2);
1153
1154 CONVERT_CHECKED(JSFunction, fun, args[0]);
1155 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1156
1157 Code* code = fun->code();
1158 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1159
1160 Address pc = code->address() + offset;
1161 return Smi::FromInt(fun->code()->SourcePosition(pc));
1162}
1163
1164
1165
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001166static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1167 NoHandleAllocation ha;
1168 ASSERT(args.length() == 2);
1169
1170 CONVERT_CHECKED(JSFunction, fun, args[0]);
1171 CONVERT_CHECKED(String, name, args[1]);
1172 fun->SetInstanceClassName(name);
1173 return Heap::undefined_value();
1174}
1175
1176
1177static Object* Runtime_FunctionSetLength(Arguments args) {
1178 NoHandleAllocation ha;
1179 ASSERT(args.length() == 2);
1180
1181 CONVERT_CHECKED(JSFunction, fun, args[0]);
1182 CONVERT_CHECKED(Smi, length, args[1]);
1183 fun->shared()->set_length(length->value());
1184 return length;
1185}
1186
1187
1188static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001189 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 ASSERT(args.length() == 2);
1191
1192 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001193 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1194 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001195 return args[0]; // return TOS
1196}
1197
1198
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001199static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1200 NoHandleAllocation ha;
1201 ASSERT(args.length() == 1);
1202
1203 CONVERT_CHECKED(JSFunction, f, args[0]);
1204 // The function_data field of the shared function info is used exclusively by
1205 // the API.
1206 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1207 : Heap::false_value();
1208}
1209
1210
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001211static Object* Runtime_SetCode(Arguments args) {
1212 HandleScope scope;
1213 ASSERT(args.length() == 2);
1214
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001215 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001216 Handle<Object> code = args.at<Object>(1);
1217
1218 Handle<Context> context(target->context());
1219
1220 if (!code->IsNull()) {
1221 RUNTIME_ASSERT(code->IsJSFunction());
1222 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1223 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1224 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1225 return Failure::Exception();
1226 }
1227 // Set the code, formal parameter count, and the length of the target
1228 // function.
1229 target->set_code(fun->code());
1230 target->shared()->set_length(fun->shared()->length());
1231 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001232 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001233 // Set the source code of the target function to undefined.
1234 // SetCode is only used for built-in constructors like String,
1235 // Array, and Object, and some web code
1236 // doesn't like seeing source code for constructors.
1237 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001238 context = Handle<Context>(fun->context());
1239
1240 // Make sure we get a fresh copy of the literal vector to avoid
1241 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001242 int number_of_literals = fun->NumberOfLiterals();
1243 Handle<FixedArray> literals =
1244 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001245 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001246 // Insert the object, regexp and array functions in the literals
1247 // array prefix. These are the functions that will be used when
1248 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001249 literals->set(JSFunction::kLiteralGlobalContextIndex,
1250 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001251 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001252 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001253 }
1254
1255 target->set_context(*context);
1256 return *target;
1257}
1258
1259
1260static Object* CharCodeAt(String* subject, Object* index) {
1261 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001262 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001263 // Flatten the string. If someone wants to get a char at an index
1264 // in a cons string, it is likely that more indices will be
1265 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001266 subject->TryFlattenIfNotFlat();
1267 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001268 return Heap::nan_value();
1269 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001270 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001271}
1272
1273
1274static Object* Runtime_StringCharCodeAt(Arguments args) {
1275 NoHandleAllocation ha;
1276 ASSERT(args.length() == 2);
1277
1278 CONVERT_CHECKED(String, subject, args[0]);
1279 Object* index = args[1];
1280 return CharCodeAt(subject, index);
1281}
1282
1283
1284static Object* Runtime_CharFromCode(Arguments args) {
1285 NoHandleAllocation ha;
1286 ASSERT(args.length() == 1);
1287 uint32_t code;
1288 if (Array::IndexFromObject(args[0], &code)) {
1289 if (code <= 0xffff) {
1290 return Heap::LookupSingleCharacterStringFromCode(code);
1291 }
1292 }
1293 return Heap::empty_string();
1294}
1295
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001296// Forward declarations.
1297static const int kStringBuilderConcatHelperLengthBits = 11;
1298static const int kStringBuilderConcatHelperPositionBits = 19;
1299
1300template <typename schar>
1301static inline void StringBuilderConcatHelper(String*,
1302 schar*,
1303 FixedArray*,
1304 int);
1305
1306typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1307typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1308
1309class ReplacementStringBuilder {
1310 public:
1311 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1312 : subject_(subject),
1313 parts_(Factory::NewFixedArray(estimated_part_count)),
1314 part_count_(0),
1315 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001316 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001317 // Require a non-zero initial size. Ensures that doubling the size to
1318 // extend the array will work.
1319 ASSERT(estimated_part_count > 0);
1320 }
1321
1322 void EnsureCapacity(int elements) {
1323 int length = parts_->length();
1324 int required_length = part_count_ + elements;
1325 if (length < required_length) {
1326 int new_length = length;
1327 do {
1328 new_length *= 2;
1329 } while (new_length < required_length);
1330 Handle<FixedArray> extended_array =
1331 Factory::NewFixedArray(new_length);
1332 parts_->CopyTo(0, *extended_array, 0, part_count_);
1333 parts_ = extended_array;
1334 }
1335 }
1336
1337 void AddSubjectSlice(int from, int to) {
1338 ASSERT(from >= 0);
1339 int length = to - from;
1340 ASSERT(length > 0);
1341 // Can we encode the slice in 11 bits for length and 19 bits for
1342 // start position - as used by StringBuilderConcatHelper?
1343 if (StringBuilderSubstringLength::is_valid(length) &&
1344 StringBuilderSubstringPosition::is_valid(from)) {
1345 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1346 StringBuilderSubstringPosition::encode(from);
1347 AddElement(Smi::FromInt(encoded_slice));
1348 } else {
1349 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1350 AddElement(*slice);
1351 }
1352 IncrementCharacterCount(length);
1353 }
1354
1355
1356 void AddString(Handle<String> string) {
1357 int length = string->length();
1358 ASSERT(length > 0);
1359 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001360 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001361 is_ascii_ = false;
1362 }
1363 IncrementCharacterCount(length);
1364 }
1365
1366
1367 Handle<String> ToString() {
1368 if (part_count_ == 0) {
1369 return Factory::empty_string();
1370 }
1371
1372 Handle<String> joined_string;
1373 if (is_ascii_) {
1374 joined_string = NewRawAsciiString(character_count_);
1375 AssertNoAllocation no_alloc;
1376 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1377 char* char_buffer = seq->GetChars();
1378 StringBuilderConcatHelper(*subject_,
1379 char_buffer,
1380 *parts_,
1381 part_count_);
1382 } else {
1383 // Non-ASCII.
1384 joined_string = NewRawTwoByteString(character_count_);
1385 AssertNoAllocation no_alloc;
1386 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1387 uc16* char_buffer = seq->GetChars();
1388 StringBuilderConcatHelper(*subject_,
1389 char_buffer,
1390 *parts_,
1391 part_count_);
1392 }
1393 return joined_string;
1394 }
1395
1396
1397 void IncrementCharacterCount(int by) {
1398 if (character_count_ > Smi::kMaxValue - by) {
1399 V8::FatalProcessOutOfMemory("String.replace result too large.");
1400 }
1401 character_count_ += by;
1402 }
1403
1404 private:
1405
1406 Handle<String> NewRawAsciiString(int size) {
1407 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1408 }
1409
1410
1411 Handle<String> NewRawTwoByteString(int size) {
1412 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1413 }
1414
1415
1416 void AddElement(Object* element) {
1417 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001418 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001419 parts_->set(part_count_, element);
1420 part_count_++;
1421 }
1422
1423 Handle<String> subject_;
1424 Handle<FixedArray> parts_;
1425 int part_count_;
1426 int character_count_;
1427 bool is_ascii_;
1428};
1429
1430
1431class CompiledReplacement {
1432 public:
1433 CompiledReplacement()
1434 : parts_(1), replacement_substrings_(0) {}
1435
1436 void Compile(Handle<String> replacement,
1437 int capture_count,
1438 int subject_length);
1439
1440 void Apply(ReplacementStringBuilder* builder,
1441 int match_from,
1442 int match_to,
1443 Handle<JSArray> last_match_info);
1444
1445 // Number of distinct parts of the replacement pattern.
1446 int parts() {
1447 return parts_.length();
1448 }
1449 private:
1450 enum PartType {
1451 SUBJECT_PREFIX = 1,
1452 SUBJECT_SUFFIX,
1453 SUBJECT_CAPTURE,
1454 REPLACEMENT_SUBSTRING,
1455 REPLACEMENT_STRING,
1456
1457 NUMBER_OF_PART_TYPES
1458 };
1459
1460 struct ReplacementPart {
1461 static inline ReplacementPart SubjectMatch() {
1462 return ReplacementPart(SUBJECT_CAPTURE, 0);
1463 }
1464 static inline ReplacementPart SubjectCapture(int capture_index) {
1465 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1466 }
1467 static inline ReplacementPart SubjectPrefix() {
1468 return ReplacementPart(SUBJECT_PREFIX, 0);
1469 }
1470 static inline ReplacementPart SubjectSuffix(int subject_length) {
1471 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1472 }
1473 static inline ReplacementPart ReplacementString() {
1474 return ReplacementPart(REPLACEMENT_STRING, 0);
1475 }
1476 static inline ReplacementPart ReplacementSubString(int from, int to) {
1477 ASSERT(from >= 0);
1478 ASSERT(to > from);
1479 return ReplacementPart(-from, to);
1480 }
1481
1482 // If tag <= 0 then it is the negation of a start index of a substring of
1483 // the replacement pattern, otherwise it's a value from PartType.
1484 ReplacementPart(int tag, int data)
1485 : tag(tag), data(data) {
1486 // Must be non-positive or a PartType value.
1487 ASSERT(tag < NUMBER_OF_PART_TYPES);
1488 }
1489 // Either a value of PartType or a non-positive number that is
1490 // the negation of an index into the replacement string.
1491 int tag;
1492 // The data value's interpretation depends on the value of tag:
1493 // tag == SUBJECT_PREFIX ||
1494 // tag == SUBJECT_SUFFIX: data is unused.
1495 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1496 // tag == REPLACEMENT_SUBSTRING ||
1497 // tag == REPLACEMENT_STRING: data is index into array of substrings
1498 // of the replacement string.
1499 // tag <= 0: Temporary representation of the substring of the replacement
1500 // string ranging over -tag .. data.
1501 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1502 // substring objects.
1503 int data;
1504 };
1505
1506 template<typename Char>
1507 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1508 Vector<Char> characters,
1509 int capture_count,
1510 int subject_length) {
1511 int length = characters.length();
1512 int last = 0;
1513 for (int i = 0; i < length; i++) {
1514 Char c = characters[i];
1515 if (c == '$') {
1516 int next_index = i + 1;
1517 if (next_index == length) { // No next character!
1518 break;
1519 }
1520 Char c2 = characters[next_index];
1521 switch (c2) {
1522 case '$':
1523 if (i > last) {
1524 // There is a substring before. Include the first "$".
1525 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1526 last = next_index + 1; // Continue after the second "$".
1527 } else {
1528 // Let the next substring start with the second "$".
1529 last = next_index;
1530 }
1531 i = next_index;
1532 break;
1533 case '`':
1534 if (i > last) {
1535 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1536 }
1537 parts->Add(ReplacementPart::SubjectPrefix());
1538 i = next_index;
1539 last = i + 1;
1540 break;
1541 case '\'':
1542 if (i > last) {
1543 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1544 }
1545 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1546 i = next_index;
1547 last = i + 1;
1548 break;
1549 case '&':
1550 if (i > last) {
1551 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1552 }
1553 parts->Add(ReplacementPart::SubjectMatch());
1554 i = next_index;
1555 last = i + 1;
1556 break;
1557 case '0':
1558 case '1':
1559 case '2':
1560 case '3':
1561 case '4':
1562 case '5':
1563 case '6':
1564 case '7':
1565 case '8':
1566 case '9': {
1567 int capture_ref = c2 - '0';
1568 if (capture_ref > capture_count) {
1569 i = next_index;
1570 continue;
1571 }
1572 int second_digit_index = next_index + 1;
1573 if (second_digit_index < length) {
1574 // Peek ahead to see if we have two digits.
1575 Char c3 = characters[second_digit_index];
1576 if ('0' <= c3 && c3 <= '9') { // Double digits.
1577 int double_digit_ref = capture_ref * 10 + c3 - '0';
1578 if (double_digit_ref <= capture_count) {
1579 next_index = second_digit_index;
1580 capture_ref = double_digit_ref;
1581 }
1582 }
1583 }
1584 if (capture_ref > 0) {
1585 if (i > last) {
1586 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1587 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001588 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001589 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1590 last = next_index + 1;
1591 }
1592 i = next_index;
1593 break;
1594 }
1595 default:
1596 i = next_index;
1597 break;
1598 }
1599 }
1600 }
1601 if (length > last) {
1602 if (last == 0) {
1603 parts->Add(ReplacementPart::ReplacementString());
1604 } else {
1605 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1606 }
1607 }
1608 }
1609
1610 ZoneList<ReplacementPart> parts_;
1611 ZoneList<Handle<String> > replacement_substrings_;
1612};
1613
1614
1615void CompiledReplacement::Compile(Handle<String> replacement,
1616 int capture_count,
1617 int subject_length) {
1618 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001619 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001620 AssertNoAllocation no_alloc;
1621 ParseReplacementPattern(&parts_,
1622 replacement->ToAsciiVector(),
1623 capture_count,
1624 subject_length);
1625 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001626 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001627 AssertNoAllocation no_alloc;
1628
1629 ParseReplacementPattern(&parts_,
1630 replacement->ToUC16Vector(),
1631 capture_count,
1632 subject_length);
1633 }
1634 // Find substrings of replacement string and create them as String objects..
1635 int substring_index = 0;
1636 for (int i = 0, n = parts_.length(); i < n; i++) {
1637 int tag = parts_[i].tag;
1638 if (tag <= 0) { // A replacement string slice.
1639 int from = -tag;
1640 int to = parts_[i].data;
1641 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1642 from,
1643 to));
1644 parts_[i].tag = REPLACEMENT_SUBSTRING;
1645 parts_[i].data = substring_index;
1646 substring_index++;
1647 } else if (tag == REPLACEMENT_STRING) {
1648 replacement_substrings_.Add(replacement);
1649 parts_[i].data = substring_index;
1650 substring_index++;
1651 }
1652 }
1653}
1654
1655
1656void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1657 int match_from,
1658 int match_to,
1659 Handle<JSArray> last_match_info) {
1660 for (int i = 0, n = parts_.length(); i < n; i++) {
1661 ReplacementPart part = parts_[i];
1662 switch (part.tag) {
1663 case SUBJECT_PREFIX:
1664 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1665 break;
1666 case SUBJECT_SUFFIX: {
1667 int subject_length = part.data;
1668 if (match_to < subject_length) {
1669 builder->AddSubjectSlice(match_to, subject_length);
1670 }
1671 break;
1672 }
1673 case SUBJECT_CAPTURE: {
1674 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001675 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001676 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1677 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1678 if (from >= 0 && to > from) {
1679 builder->AddSubjectSlice(from, to);
1680 }
1681 break;
1682 }
1683 case REPLACEMENT_SUBSTRING:
1684 case REPLACEMENT_STRING:
1685 builder->AddString(replacement_substrings_[part.data]);
1686 break;
1687 default:
1688 UNREACHABLE();
1689 }
1690 }
1691}
1692
1693
1694
1695static Object* StringReplaceRegExpWithString(String* subject,
1696 JSRegExp* regexp,
1697 String* replacement,
1698 JSArray* last_match_info) {
1699 ASSERT(subject->IsFlat());
1700 ASSERT(replacement->IsFlat());
1701
1702 HandleScope handles;
1703
1704 int length = subject->length();
1705 Handle<String> subject_handle(subject);
1706 Handle<JSRegExp> regexp_handle(regexp);
1707 Handle<String> replacement_handle(replacement);
1708 Handle<JSArray> last_match_info_handle(last_match_info);
1709 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1710 subject_handle,
1711 0,
1712 last_match_info_handle);
1713 if (match.is_null()) {
1714 return Failure::Exception();
1715 }
1716 if (match->IsNull()) {
1717 return *subject_handle;
1718 }
1719
1720 int capture_count = regexp_handle->CaptureCount();
1721
1722 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001723 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001724 CompiledReplacement compiled_replacement;
1725 compiled_replacement.Compile(replacement_handle,
1726 capture_count,
1727 length);
1728
1729 bool is_global = regexp_handle->GetFlags().is_global();
1730
1731 // Guessing the number of parts that the final result string is built
1732 // from. Global regexps can match any number of times, so we guess
1733 // conservatively.
1734 int expected_parts =
1735 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1736 ReplacementStringBuilder builder(subject_handle, expected_parts);
1737
1738 // Index of end of last match.
1739 int prev = 0;
1740
1741 // Number of parts added by compiled replacement plus preceeding string
1742 // and possibly suffix after last match.
1743 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1744 bool matched = true;
1745 do {
1746 ASSERT(last_match_info_handle->HasFastElements());
1747 // Increase the capacity of the builder before entering local handle-scope,
1748 // so its internal buffer can safely allocate a new handle if it grows.
1749 builder.EnsureCapacity(parts_added_per_loop);
1750
1751 HandleScope loop_scope;
1752 int start, end;
1753 {
1754 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001755 FixedArray* match_info_array =
1756 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001757
1758 ASSERT_EQ(capture_count * 2 + 2,
1759 RegExpImpl::GetLastCaptureCount(match_info_array));
1760 start = RegExpImpl::GetCapture(match_info_array, 0);
1761 end = RegExpImpl::GetCapture(match_info_array, 1);
1762 }
1763
1764 if (prev < start) {
1765 builder.AddSubjectSlice(prev, start);
1766 }
1767 compiled_replacement.Apply(&builder,
1768 start,
1769 end,
1770 last_match_info_handle);
1771 prev = end;
1772
1773 // Only continue checking for global regexps.
1774 if (!is_global) break;
1775
1776 // Continue from where the match ended, unless it was an empty match.
1777 int next = end;
1778 if (start == end) {
1779 next = end + 1;
1780 if (next > length) break;
1781 }
1782
1783 match = RegExpImpl::Exec(regexp_handle,
1784 subject_handle,
1785 next,
1786 last_match_info_handle);
1787 if (match.is_null()) {
1788 return Failure::Exception();
1789 }
1790 matched = !match->IsNull();
1791 } while (matched);
1792
1793 if (prev < length) {
1794 builder.AddSubjectSlice(prev, length);
1795 }
1796
1797 return *(builder.ToString());
1798}
1799
1800
1801static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1802 ASSERT(args.length() == 4);
1803
1804 CONVERT_CHECKED(String, subject, args[0]);
1805 if (!subject->IsFlat()) {
1806 Object* flat_subject = subject->TryFlatten();
1807 if (flat_subject->IsFailure()) {
1808 return flat_subject;
1809 }
1810 subject = String::cast(flat_subject);
1811 }
1812
1813 CONVERT_CHECKED(String, replacement, args[2]);
1814 if (!replacement->IsFlat()) {
1815 Object* flat_replacement = replacement->TryFlatten();
1816 if (flat_replacement->IsFailure()) {
1817 return flat_replacement;
1818 }
1819 replacement = String::cast(flat_replacement);
1820 }
1821
1822 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1823 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1824
1825 ASSERT(last_match_info->HasFastElements());
1826
1827 return StringReplaceRegExpWithString(subject,
1828 regexp,
1829 replacement,
1830 last_match_info);
1831}
1832
1833
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001834
ager@chromium.org7c537e22008-10-16 08:43:32 +00001835// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1836// limit, we can fix the size of tables.
1837static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001838// Reduce alphabet to this size.
1839static const int kBMAlphabetSize = 0x100;
1840// For patterns below this length, the skip length of Boyer-Moore is too short
1841// to compensate for the algorithmic overhead compared to simple brute force.
1842static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001843
ager@chromium.org7c537e22008-10-16 08:43:32 +00001844// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1845// shift. Only allows the last kBMMaxShift characters of the needle
1846// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001847class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001848 public:
1849 BMGoodSuffixBuffers() {}
1850 inline void init(int needle_length) {
1851 ASSERT(needle_length > 1);
1852 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1853 int len = needle_length - start;
1854 biased_suffixes_ = suffixes_ - start;
1855 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1856 for (int i = 0; i <= len; i++) {
1857 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001858 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001859 }
1860 inline int& suffix(int index) {
1861 ASSERT(biased_suffixes_ + index >= suffixes_);
1862 return biased_suffixes_[index];
1863 }
1864 inline int& shift(int index) {
1865 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1866 return biased_good_suffix_shift_[index];
1867 }
1868 private:
1869 int suffixes_[kBMMaxShift + 1];
1870 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001871 int* biased_suffixes_;
1872 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001873 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1874};
1875
1876// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001877static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001878static BMGoodSuffixBuffers bmgs_buffers;
1879
1880// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001881template <typename pchar>
1882static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1883 int start) {
1884 // Run forwards to populate bad_char_table, so that *last* instance
1885 // of character equivalence class is the one registered.
1886 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001887 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1888 : kBMAlphabetSize;
1889 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001890 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001891 } else {
1892 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001893 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001894 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001895 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001896 for (int i = start; i < pattern.length() - 1; i++) {
1897 pchar c = pattern[i];
1898 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001899 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001900 }
1901}
1902
1903template <typename pchar>
1904static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001905 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001906 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001907 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001908 // Compute Good Suffix tables.
1909 bmgs_buffers.init(m);
1910
1911 bmgs_buffers.shift(m-1) = 1;
1912 bmgs_buffers.suffix(m) = m + 1;
1913 pchar last_char = pattern[m - 1];
1914 int suffix = m + 1;
1915 for (int i = m; i > start;) {
1916 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1917 if (bmgs_buffers.shift(suffix) == len) {
1918 bmgs_buffers.shift(suffix) = suffix - i;
1919 }
1920 suffix = bmgs_buffers.suffix(suffix);
1921 }
1922 i--;
1923 suffix--;
1924 bmgs_buffers.suffix(i) = suffix;
1925 if (suffix == m) {
1926 // No suffix to extend, so we check against last_char only.
1927 while (i > start && pattern[i - 1] != last_char) {
1928 if (bmgs_buffers.shift(m) == len) {
1929 bmgs_buffers.shift(m) = m - i;
1930 }
1931 i--;
1932 bmgs_buffers.suffix(i) = m;
1933 }
1934 if (i > start) {
1935 i--;
1936 suffix--;
1937 bmgs_buffers.suffix(i) = suffix;
1938 }
1939 }
1940 }
1941 if (suffix < m) {
1942 for (int i = start; i <= m; i++) {
1943 if (bmgs_buffers.shift(i) == len) {
1944 bmgs_buffers.shift(i) = suffix - start;
1945 }
1946 if (i == suffix) {
1947 suffix = bmgs_buffers.suffix(suffix);
1948 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001949 }
1950 }
1951}
1952
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001953template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001954static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001955 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001956 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001957 }
1958 if (sizeof(pchar) == 1) {
1959 if (char_code > String::kMaxAsciiCharCode) {
1960 return -1;
1961 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001962 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001963 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001964 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001965}
1966
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967// Restricted simplified Boyer-Moore string matching.
1968// Uses only the bad-shift table of Boyer-Moore and only uses it
1969// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001970template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001971static int BoyerMooreHorspool(Vector<const schar> subject,
1972 Vector<const pchar> pattern,
1973 int start_index,
1974 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001975 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001976 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001977 // Only preprocess at most kBMMaxShift last characters of pattern.
1978 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001979
ager@chromium.org7c537e22008-10-16 08:43:32 +00001980 BoyerMoorePopulateBadCharTable(pattern, start);
1981
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001982 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001983 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001984 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001985 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001986 // Perform search
1987 for (idx = start_index; idx <= n - m;) {
1988 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001989 int c;
1990 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001991 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001992 int shift = j - bc_occ;
1993 idx += shift;
1994 badness += 1 - shift; // at most zero, so badness cannot increase.
1995 if (idx > n - m) {
1996 *complete = true;
1997 return -1;
1998 }
1999 }
2000 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002001 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002002 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002003 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002004 return idx;
2005 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002006 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002007 // Badness increases by the number of characters we have
2008 // checked, and decreases by the number of characters we
2009 // can skip by shifting. It's a measure of how we are doing
2010 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002011 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002012 if (badness > 0) {
2013 *complete = false;
2014 return idx;
2015 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002016 }
2017 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002018 *complete = true;
2019 return -1;
2020}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002021
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002022
2023template <typename schar, typename pchar>
2024static int BoyerMooreIndexOf(Vector<const schar> subject,
2025 Vector<const pchar> pattern,
2026 int idx) {
2027 int n = subject.length();
2028 int m = pattern.length();
2029 // Only preprocess at most kBMMaxShift last characters of pattern.
2030 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2031
2032 // Build the Good Suffix table and continue searching.
2033 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2034 pchar last_char = pattern[m - 1];
2035 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002036 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002037 int j = m - 1;
2038 schar c;
2039 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002040 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002041 idx += shift;
2042 if (idx > n - m) {
2043 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002044 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002045 }
2046 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2047 if (j < 0) {
2048 return idx;
2049 } else if (j < start) {
2050 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002051 // Fall back on BMH shift.
2052 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002053 } else {
2054 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002055 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002056 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002057 if (gs_shift > shift) {
2058 shift = gs_shift;
2059 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002060 idx += shift;
2061 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002062 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002063
2064 return -1;
2065}
2066
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002067
2068template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002069static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002070 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002071 int start_index) {
2072 for (int i = start_index, n = string.length(); i < n; i++) {
2073 if (pattern_char == string[i]) {
2074 return i;
2075 }
2076 }
2077 return -1;
2078}
2079
2080// Trivial string search for shorter strings.
2081// On return, if "complete" is set to true, the return value is the
2082// final result of searching for the patter in the subject.
2083// If "complete" is set to false, the return value is the index where
2084// further checking should start, i.e., it's guaranteed that the pattern
2085// does not occur at a position prior to the returned index.
2086template <typename pchar, typename schar>
2087static int SimpleIndexOf(Vector<const schar> subject,
2088 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002089 int idx,
2090 bool* complete) {
2091 // Badness is a count of how much work we have done. When we have
2092 // done enough work we decide it's probably worth switching to a better
2093 // algorithm.
2094 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002095 // We know our pattern is at least 2 characters, we cache the first so
2096 // the common case of the first character not matching is faster.
2097 pchar pattern_first_char = pattern[0];
2098
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002099 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2100 badness++;
2101 if (badness > 0) {
2102 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002103 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002104 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002105 if (subject[i] != pattern_first_char) continue;
2106 int j = 1;
2107 do {
2108 if (pattern[j] != subject[i+j]) {
2109 break;
2110 }
2111 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002112 } while (j < pattern.length());
2113 if (j == pattern.length()) {
2114 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002115 return i;
2116 }
2117 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002118 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002119 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002120 return -1;
2121}
2122
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002123// Simple indexOf that never bails out. For short patterns only.
2124template <typename pchar, typename schar>
2125static int SimpleIndexOf(Vector<const schar> subject,
2126 Vector<const pchar> pattern,
2127 int idx) {
2128 pchar pattern_first_char = pattern[0];
2129 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2130 if (subject[i] != pattern_first_char) continue;
2131 int j = 1;
2132 do {
2133 if (pattern[j] != subject[i+j]) {
2134 break;
2135 }
2136 j++;
2137 } while (j < pattern.length());
2138 if (j == pattern.length()) {
2139 return i;
2140 }
2141 }
2142 return -1;
2143}
2144
2145
2146// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002147template <typename schar, typename pchar>
2148static int StringMatchStrategy(Vector<const schar> sub,
2149 Vector<const pchar> pat,
2150 int start_index) {
2151 ASSERT(pat.length() > 1);
2152
2153 // We have an ASCII haystack and a non-ASCII needle. Check if there
2154 // really is a non-ASCII character in the needle and bail out if there
2155 // is.
2156 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2157 for (int i = 0; i < pat.length(); i++) {
2158 uc16 c = pat[i];
2159 if (c > String::kMaxAsciiCharCode) {
2160 return -1;
2161 }
2162 }
2163 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002164 if (pat.length() < kBMMinPatternLength) {
2165 // We don't believe fancy searching can ever be more efficient.
2166 // The max shift of Boyer-Moore on a pattern of this length does
2167 // not compensate for the overhead.
2168 return SimpleIndexOf(sub, pat, start_index);
2169 }
2170 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002171 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002172 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2173 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002174 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002175 if (complete) return idx;
2176 return BoyerMooreIndexOf(sub, pat, idx);
2177}
2178
2179// Perform string match of pattern on subject, starting at start index.
2180// Caller must ensure that 0 <= start_index <= sub->length(),
2181// and should check that pat->length() + start_index <= sub->length()
2182int Runtime::StringMatch(Handle<String> sub,
2183 Handle<String> pat,
2184 int start_index) {
2185 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002186 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002187
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002188 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002189 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002190
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002191 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002192 if (start_index + pattern_length > subject_length) return -1;
2193
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002194 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002195 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002196 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002197 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002198 // character patterns linear search is necessary, so any smart
2199 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002200 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002201 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002202 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002203 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002204 if (pchar > String::kMaxAsciiCharCode) {
2205 return -1;
2206 }
2207 Vector<const char> ascii_vector =
2208 sub->ToAsciiVector().SubVector(start_index, subject_length);
2209 const void* pos = memchr(ascii_vector.start(),
2210 static_cast<const char>(pchar),
2211 static_cast<size_t>(ascii_vector.length()));
2212 if (pos == NULL) {
2213 return -1;
2214 }
2215 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2216 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002217 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002218 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002219 }
2220
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002221 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002222 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002223 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002224
ager@chromium.org7c537e22008-10-16 08:43:32 +00002225 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2226 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002227 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002228 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002229 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002230 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002231 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002232 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002233 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002234 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002235 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002236 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002237 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002238 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002239}
2240
2241
2242static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002243 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002244 ASSERT(args.length() == 3);
2245
ager@chromium.org7c537e22008-10-16 08:43:32 +00002246 CONVERT_ARG_CHECKED(String, sub, 0);
2247 CONVERT_ARG_CHECKED(String, pat, 1);
2248
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002249 Object* index = args[2];
2250 uint32_t start_index;
2251 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2252
ager@chromium.org870a0b62008-11-04 11:43:05 +00002253 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002254 int position = Runtime::StringMatch(sub, pat, start_index);
2255 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002256}
2257
2258
2259static Object* Runtime_StringLastIndexOf(Arguments args) {
2260 NoHandleAllocation ha;
2261 ASSERT(args.length() == 3);
2262
2263 CONVERT_CHECKED(String, sub, args[0]);
2264 CONVERT_CHECKED(String, pat, args[1]);
2265 Object* index = args[2];
2266
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002267 sub->TryFlattenIfNotFlat();
2268 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002269
2270 uint32_t start_index;
2271 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2272
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002273 uint32_t pattern_length = pat->length();
2274 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002275
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002276 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002277 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002278 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002279
2280 for (int i = start_index; i >= 0; i--) {
2281 bool found = true;
2282 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002283 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002284 found = false;
2285 break;
2286 }
2287 }
2288 if (found) return Smi::FromInt(i);
2289 }
2290
2291 return Smi::FromInt(-1);
2292}
2293
2294
2295static Object* Runtime_StringLocaleCompare(Arguments args) {
2296 NoHandleAllocation ha;
2297 ASSERT(args.length() == 2);
2298
2299 CONVERT_CHECKED(String, str1, args[0]);
2300 CONVERT_CHECKED(String, str2, args[1]);
2301
2302 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002303 int str1_length = str1->length();
2304 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002305
2306 // Decide trivial cases without flattening.
2307 if (str1_length == 0) {
2308 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2309 return Smi::FromInt(-str2_length);
2310 } else {
2311 if (str2_length == 0) return Smi::FromInt(str1_length);
2312 }
2313
2314 int end = str1_length < str2_length ? str1_length : str2_length;
2315
2316 // No need to flatten if we are going to find the answer on the first
2317 // character. At this point we know there is at least one character
2318 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002319 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002320 if (d != 0) return Smi::FromInt(d);
2321
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002322 str1->TryFlattenIfNotFlat();
2323 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002324
2325 static StringInputBuffer buf1;
2326 static StringInputBuffer buf2;
2327
2328 buf1.Reset(str1);
2329 buf2.Reset(str2);
2330
2331 for (int i = 0; i < end; i++) {
2332 uint16_t char1 = buf1.GetNext();
2333 uint16_t char2 = buf2.GetNext();
2334 if (char1 != char2) return Smi::FromInt(char1 - char2);
2335 }
2336
2337 return Smi::FromInt(str1_length - str2_length);
2338}
2339
2340
2341static Object* Runtime_StringSlice(Arguments args) {
2342 NoHandleAllocation ha;
2343 ASSERT(args.length() == 3);
2344
2345 CONVERT_CHECKED(String, value, args[0]);
2346 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2347 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2348
2349 int start = FastD2I(from_number);
2350 int end = FastD2I(to_number);
2351
2352 RUNTIME_ASSERT(end >= start);
2353 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002354 RUNTIME_ASSERT(end <= value->length());
2355 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002356}
2357
2358
ager@chromium.org41826e72009-03-30 13:30:57 +00002359static Object* Runtime_StringMatch(Arguments args) {
2360 ASSERT_EQ(3, args.length());
2361
2362 CONVERT_ARG_CHECKED(String, subject, 0);
2363 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2364 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2365 HandleScope handles;
2366
2367 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2368
2369 if (match.is_null()) {
2370 return Failure::Exception();
2371 }
2372 if (match->IsNull()) {
2373 return Heap::null_value();
2374 }
2375 int length = subject->length();
2376
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002377 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002378 ZoneList<int> offsets(8);
2379 do {
2380 int start;
2381 int end;
2382 {
2383 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002384 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002385 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2386 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2387 }
2388 offsets.Add(start);
2389 offsets.Add(end);
2390 int index = start < end ? end : end + 1;
2391 if (index > length) break;
2392 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2393 if (match.is_null()) {
2394 return Failure::Exception();
2395 }
2396 } while (!match->IsNull());
2397 int matches = offsets.length() / 2;
2398 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2399 for (int i = 0; i < matches ; i++) {
2400 int from = offsets.at(i * 2);
2401 int to = offsets.at(i * 2 + 1);
2402 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2403 }
2404 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2405 result->set_length(Smi::FromInt(matches));
2406 return *result;
2407}
2408
2409
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410static Object* Runtime_NumberToRadixString(Arguments args) {
2411 NoHandleAllocation ha;
2412 ASSERT(args.length() == 2);
2413
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002414 // Fast case where the result is a one character string.
2415 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2416 int value = Smi::cast(args[0])->value();
2417 int radix = Smi::cast(args[1])->value();
2418 if (value >= 0 && value < radix) {
2419 RUNTIME_ASSERT(radix <= 36);
2420 // Character array used for conversion.
2421 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2422 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2423 }
2424 }
2425
2426 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002427 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(radix_number, args[1]);
2438 int radix = FastD2I(radix_number);
2439 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2440 char* str = DoubleToRadixCString(value, radix);
2441 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2442 DeleteArray(str);
2443 return result;
2444}
2445
2446
2447static Object* Runtime_NumberToFixed(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 >= 0);
2464 char* str = DoubleToFixedCString(value, f);
2465 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2466 DeleteArray(str);
2467 return res;
2468}
2469
2470
2471static Object* Runtime_NumberToExponential(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 <= 20);
2488 char* str = DoubleToExponentialCString(value, f);
2489 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2490 DeleteArray(str);
2491 return res;
2492}
2493
2494
2495static Object* Runtime_NumberToPrecision(Arguments args) {
2496 NoHandleAllocation ha;
2497 ASSERT(args.length() == 2);
2498
2499 CONVERT_DOUBLE_CHECKED(value, args[0]);
2500 if (isnan(value)) {
2501 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2502 }
2503 if (isinf(value)) {
2504 if (value < 0) {
2505 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2506 }
2507 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2508 }
2509 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2510 int f = FastD2I(f_number);
2511 RUNTIME_ASSERT(f >= 1 && f <= 21);
2512 char* str = DoubleToPrecisionCString(value, f);
2513 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2514 DeleteArray(str);
2515 return res;
2516}
2517
2518
2519// Returns a single character string where first character equals
2520// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002521static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002522 if (index < static_cast<uint32_t>(string->length())) {
2523 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002524 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002525 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002527 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002528}
2529
2530
2531Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2532 // Handle [] indexing on Strings
2533 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002534 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2535 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002536 }
2537
2538 // Handle [] indexing on String objects
2539 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002540 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2541 Handle<Object> result =
2542 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2543 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002544 }
2545
2546 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002547 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002548 return prototype->GetElement(index);
2549 }
2550
2551 return object->GetElement(index);
2552}
2553
2554
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002555Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2556 HandleScope scope;
2557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002558 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002559 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002560 Handle<Object> error =
2561 Factory::NewTypeError("non_object_property_load",
2562 HandleVector(args, 2));
2563 return Top::Throw(*error);
2564 }
2565
2566 // Check if the given key is an array index.
2567 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002568 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569 return GetElementOrCharAt(object, index);
2570 }
2571
2572 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002573 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002574 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002575 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002576 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002577 bool has_pending_exception = false;
2578 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002579 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002580 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002581 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002582 }
2583
ager@chromium.org32912102009-01-16 10:38:43 +00002584 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002585 // the element if so.
2586 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002587 return GetElementOrCharAt(object, index);
2588 } else {
2589 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002590 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002591 }
2592}
2593
2594
2595static Object* Runtime_GetProperty(Arguments args) {
2596 NoHandleAllocation ha;
2597 ASSERT(args.length() == 2);
2598
2599 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002600 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002601
2602 return Runtime::GetObjectProperty(object, key);
2603}
2604
2605
ager@chromium.org7c537e22008-10-16 08:43:32 +00002606
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002607// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002608static Object* Runtime_KeyedGetProperty(Arguments args) {
2609 NoHandleAllocation ha;
2610 ASSERT(args.length() == 2);
2611
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002612 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002613 // itself.
2614 //
2615 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002616 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002617 // global proxy object never has properties. This is the case
2618 // because the global proxy object forwards everything to its hidden
2619 // prototype including local lookups.
2620 //
2621 // Additionally, we need to make sure that we do not cache results
2622 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623 if (args[0]->IsJSObject() &&
2624 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002625 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002626 args[1]->IsString()) {
2627 JSObject* receiver = JSObject::cast(args[0]);
2628 String* key = String::cast(args[1]);
2629 if (receiver->HasFastProperties()) {
2630 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002631 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002632 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2633 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002634 Object* value = receiver->FastPropertyAt(offset);
2635 return value->IsTheHole() ? Heap::undefined_value() : value;
2636 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002637 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002638 LookupResult result;
2639 receiver->LocalLookup(key, &result);
2640 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2641 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002642 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002643 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002644 }
2645 } else {
2646 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002647 StringDictionary* dictionary = receiver->property_dictionary();
2648 int entry = dictionary->FindEntry(key);
2649 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002650 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002651 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002652 if (!receiver->IsGlobalObject()) return value;
2653 value = JSGlobalPropertyCell::cast(value)->value();
2654 if (!value->IsTheHole()) return value;
2655 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002656 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002657 }
2658 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002659
2660 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002661 return Runtime::GetObjectProperty(args.at<Object>(0),
2662 args.at<Object>(1));
2663}
2664
2665
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002666Object* Runtime::SetObjectProperty(Handle<Object> object,
2667 Handle<Object> key,
2668 Handle<Object> value,
2669 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002670 HandleScope scope;
2671
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002672 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002673 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002674 Handle<Object> error =
2675 Factory::NewTypeError("non_object_property_store",
2676 HandleVector(args, 2));
2677 return Top::Throw(*error);
2678 }
2679
2680 // If the object isn't a JavaScript object, we ignore the store.
2681 if (!object->IsJSObject()) return *value;
2682
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002683 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2684
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685 // Check if the given key is an array index.
2686 uint32_t index;
2687 if (Array::IndexFromObject(*key, &index)) {
2688 ASSERT(attr == NONE);
2689
2690 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2691 // of a string using [] notation. We need to support this too in
2692 // JavaScript.
2693 // In the case of a String object we just need to redirect the assignment to
2694 // the underlying string if the index is in range. Since the underlying
2695 // string does nothing with the assignment then we can ignore such
2696 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002697 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002699 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002700
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002701 Handle<Object> result = SetElement(js_object, index, value);
2702 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002703 return *value;
2704 }
2705
2706 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002707 Handle<Object> result;
2708 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002709 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002710 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002711 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002712 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002713 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002714 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002715 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002716 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002717 return *value;
2718 }
2719
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002720 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002721 bool has_pending_exception = false;
2722 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2723 if (has_pending_exception) return Failure::Exception();
2724 Handle<String> name = Handle<String>::cast(converted);
2725
2726 if (name->AsArrayIndex(&index)) {
2727 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002728 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002729 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002730 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002731 }
2732}
2733
2734
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002735Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2736 Handle<Object> key,
2737 Handle<Object> value,
2738 PropertyAttributes attr) {
2739 HandleScope scope;
2740
2741 // Check if the given key is an array index.
2742 uint32_t index;
2743 if (Array::IndexFromObject(*key, &index)) {
2744 ASSERT(attr == NONE);
2745
2746 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2747 // of a string using [] notation. We need to support this too in
2748 // JavaScript.
2749 // In the case of a String object we just need to redirect the assignment to
2750 // the underlying string if the index is in range. Since the underlying
2751 // string does nothing with the assignment then we can ignore such
2752 // assignments.
2753 if (js_object->IsStringObjectWithCharacterAt(index)) {
2754 return *value;
2755 }
2756
2757 return js_object->SetElement(index, *value);
2758 }
2759
2760 if (key->IsString()) {
2761 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2762 ASSERT(attr == NONE);
2763 return js_object->SetElement(index, *value);
2764 } else {
2765 Handle<String> key_string = Handle<String>::cast(key);
2766 key_string->TryFlattenIfNotFlat();
2767 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2768 *value,
2769 attr);
2770 }
2771 }
2772
2773 // Call-back into JavaScript to convert the key to a string.
2774 bool has_pending_exception = false;
2775 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2776 if (has_pending_exception) return Failure::Exception();
2777 Handle<String> name = Handle<String>::cast(converted);
2778
2779 if (name->AsArrayIndex(&index)) {
2780 ASSERT(attr == NONE);
2781 return js_object->SetElement(index, *value);
2782 } else {
2783 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2784 }
2785}
2786
2787
ager@chromium.orge2902be2009-06-08 12:21:35 +00002788Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2789 Handle<Object> key) {
2790 HandleScope scope;
2791
2792 // Check if the given key is an array index.
2793 uint32_t index;
2794 if (Array::IndexFromObject(*key, &index)) {
2795 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2796 // characters of a string using [] notation. In the case of a
2797 // String object we just need to redirect the deletion to the
2798 // underlying string if the index is in range. Since the
2799 // underlying string does nothing with the deletion, we can ignore
2800 // such deletions.
2801 if (js_object->IsStringObjectWithCharacterAt(index)) {
2802 return Heap::true_value();
2803 }
2804
2805 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2806 }
2807
2808 Handle<String> key_string;
2809 if (key->IsString()) {
2810 key_string = Handle<String>::cast(key);
2811 } else {
2812 // Call-back into JavaScript to convert the key to a string.
2813 bool has_pending_exception = false;
2814 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2815 if (has_pending_exception) return Failure::Exception();
2816 key_string = Handle<String>::cast(converted);
2817 }
2818
2819 key_string->TryFlattenIfNotFlat();
2820 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2821}
2822
2823
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002824static Object* Runtime_SetProperty(Arguments args) {
2825 NoHandleAllocation ha;
2826 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2827
2828 Handle<Object> object = args.at<Object>(0);
2829 Handle<Object> key = args.at<Object>(1);
2830 Handle<Object> value = args.at<Object>(2);
2831
2832 // Compute attributes.
2833 PropertyAttributes attributes = NONE;
2834 if (args.length() == 4) {
2835 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002836 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002837 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002838 RUNTIME_ASSERT(
2839 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2840 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841 }
2842 return Runtime::SetObjectProperty(object, key, value, attributes);
2843}
2844
2845
2846// Set a local property, even if it is READ_ONLY. If the property does not
2847// exist, it will be added with attributes NONE.
2848static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2849 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002850 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002851 CONVERT_CHECKED(JSObject, object, args[0]);
2852 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002853 // Compute attributes.
2854 PropertyAttributes attributes = NONE;
2855 if (args.length() == 4) {
2856 CONVERT_CHECKED(Smi, value_obj, args[3]);
2857 int unchecked_value = value_obj->value();
2858 // Only attribute bits should be set.
2859 RUNTIME_ASSERT(
2860 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2861 attributes = static_cast<PropertyAttributes>(unchecked_value);
2862 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002863
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002864 return object->
2865 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002866}
2867
2868
2869static Object* Runtime_DeleteProperty(Arguments args) {
2870 NoHandleAllocation ha;
2871 ASSERT(args.length() == 2);
2872
2873 CONVERT_CHECKED(JSObject, object, args[0]);
2874 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002875 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002876}
2877
2878
ager@chromium.org9085a012009-05-11 19:22:57 +00002879static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2880 Handle<String> key) {
2881 if (object->HasLocalProperty(*key)) return Heap::true_value();
2882 // Handle hidden prototypes. If there's a hidden prototype above this thing
2883 // then we have to check it for properties, because they are supposed to
2884 // look like they are on this object.
2885 Handle<Object> proto(object->GetPrototype());
2886 if (proto->IsJSObject() &&
2887 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2888 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2889 }
2890 return Heap::false_value();
2891}
2892
2893
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002894static Object* Runtime_HasLocalProperty(Arguments args) {
2895 NoHandleAllocation ha;
2896 ASSERT(args.length() == 2);
2897 CONVERT_CHECKED(String, key, args[1]);
2898
ager@chromium.org9085a012009-05-11 19:22:57 +00002899 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002900 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002901 if (obj->IsJSObject()) {
2902 JSObject* object = JSObject::cast(obj);
2903 // Fast case - no interceptors.
2904 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2905 // Slow case. Either it's not there or we have an interceptor. We should
2906 // have handles for this kind of deal.
2907 HandleScope scope;
2908 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2909 Handle<String>(key));
2910 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002911 // Well, there is one exception: Handle [] on strings.
2912 uint32_t index;
2913 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002914 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002915 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002916 return Heap::true_value();
2917 }
2918 }
2919 return Heap::false_value();
2920}
2921
2922
2923static Object* Runtime_HasProperty(Arguments args) {
2924 NoHandleAllocation na;
2925 ASSERT(args.length() == 2);
2926
2927 // Only JS objects can have properties.
2928 if (args[0]->IsJSObject()) {
2929 JSObject* object = JSObject::cast(args[0]);
2930 CONVERT_CHECKED(String, key, args[1]);
2931 if (object->HasProperty(key)) return Heap::true_value();
2932 }
2933 return Heap::false_value();
2934}
2935
2936
2937static Object* Runtime_HasElement(Arguments args) {
2938 NoHandleAllocation na;
2939 ASSERT(args.length() == 2);
2940
2941 // Only JS objects can have elements.
2942 if (args[0]->IsJSObject()) {
2943 JSObject* object = JSObject::cast(args[0]);
2944 CONVERT_CHECKED(Smi, index_obj, args[1]);
2945 uint32_t index = index_obj->value();
2946 if (object->HasElement(index)) return Heap::true_value();
2947 }
2948 return Heap::false_value();
2949}
2950
2951
2952static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2953 NoHandleAllocation ha;
2954 ASSERT(args.length() == 2);
2955
2956 CONVERT_CHECKED(JSObject, object, args[0]);
2957 CONVERT_CHECKED(String, key, args[1]);
2958
2959 uint32_t index;
2960 if (key->AsArrayIndex(&index)) {
2961 return Heap::ToBoolean(object->HasElement(index));
2962 }
2963
ager@chromium.org870a0b62008-11-04 11:43:05 +00002964 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2965 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002966}
2967
2968
2969static Object* Runtime_GetPropertyNames(Arguments args) {
2970 HandleScope scope;
2971 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002972 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002973 return *GetKeysFor(object);
2974}
2975
2976
2977// Returns either a FixedArray as Runtime_GetPropertyNames,
2978// or, if the given object has an enum cache that contains
2979// all enumerable properties of the object and its prototypes
2980// have none, the map of the object. This is used to speed up
2981// the check for deletions during a for-in.
2982static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2983 ASSERT(args.length() == 1);
2984
2985 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2986
2987 if (raw_object->IsSimpleEnum()) return raw_object->map();
2988
2989 HandleScope scope;
2990 Handle<JSObject> object(raw_object);
2991 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2992
2993 // Test again, since cache may have been built by preceding call.
2994 if (object->IsSimpleEnum()) return object->map();
2995
2996 return *content;
2997}
2998
2999
3000static Object* Runtime_GetArgumentsProperty(Arguments args) {
3001 NoHandleAllocation ha;
3002 ASSERT(args.length() == 1);
3003
3004 // Compute the frame holding the arguments.
3005 JavaScriptFrameIterator it;
3006 it.AdvanceToArgumentsFrame();
3007 JavaScriptFrame* frame = it.frame();
3008
3009 // Get the actual number of provided arguments.
3010 const uint32_t n = frame->GetProvidedParametersCount();
3011
3012 // Try to convert the key to an index. If successful and within
3013 // index return the the argument from the frame.
3014 uint32_t index;
3015 if (Array::IndexFromObject(args[0], &index) && index < n) {
3016 return frame->GetParameter(index);
3017 }
3018
3019 // Convert the key to a string.
3020 HandleScope scope;
3021 bool exception = false;
3022 Handle<Object> converted =
3023 Execution::ToString(args.at<Object>(0), &exception);
3024 if (exception) return Failure::Exception();
3025 Handle<String> key = Handle<String>::cast(converted);
3026
3027 // Try to convert the string key into an array index.
3028 if (key->AsArrayIndex(&index)) {
3029 if (index < n) {
3030 return frame->GetParameter(index);
3031 } else {
3032 return Top::initial_object_prototype()->GetElement(index);
3033 }
3034 }
3035
3036 // Handle special arguments properties.
3037 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3038 if (key->Equals(Heap::callee_symbol())) return frame->function();
3039
3040 // Lookup in the initial Object.prototype object.
3041 return Top::initial_object_prototype()->GetProperty(*key);
3042}
3043
3044
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003045static Object* Runtime_ToFastProperties(Arguments args) {
3046 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003047 Handle<Object> object = args.at<Object>(0);
3048 if (object->IsJSObject()) {
3049 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3050 js_object->TransformToFastProperties(0);
3051 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003052 return *object;
3053}
3054
3055
3056static Object* Runtime_ToSlowProperties(Arguments args) {
3057 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003058 Handle<Object> object = args.at<Object>(0);
3059 if (object->IsJSObject()) {
3060 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003061 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003062 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003063 return *object;
3064}
3065
3066
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003067static Object* Runtime_ToBool(Arguments args) {
3068 NoHandleAllocation ha;
3069 ASSERT(args.length() == 1);
3070
3071 return args[0]->ToBoolean();
3072}
3073
3074
3075// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3076// Possible optimizations: put the type string into the oddballs.
3077static Object* Runtime_Typeof(Arguments args) {
3078 NoHandleAllocation ha;
3079
3080 Object* obj = args[0];
3081 if (obj->IsNumber()) return Heap::number_symbol();
3082 HeapObject* heap_obj = HeapObject::cast(obj);
3083
3084 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003085 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003086
3087 InstanceType instance_type = heap_obj->map()->instance_type();
3088 if (instance_type < FIRST_NONSTRING_TYPE) {
3089 return Heap::string_symbol();
3090 }
3091
3092 switch (instance_type) {
3093 case ODDBALL_TYPE:
3094 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3095 return Heap::boolean_symbol();
3096 }
3097 if (heap_obj->IsNull()) {
3098 return Heap::object_symbol();
3099 }
3100 ASSERT(heap_obj->IsUndefined());
3101 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003102 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003103 return Heap::function_symbol();
3104 default:
3105 // For any kind of object not handled above, the spec rule for
3106 // host objects gives that it is okay to return "object"
3107 return Heap::object_symbol();
3108 }
3109}
3110
3111
3112static Object* Runtime_StringToNumber(Arguments args) {
3113 NoHandleAllocation ha;
3114 ASSERT(args.length() == 1);
3115 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003116 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003117 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3118}
3119
3120
3121static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3122 NoHandleAllocation ha;
3123 ASSERT(args.length() == 1);
3124
3125 CONVERT_CHECKED(JSArray, codes, args[0]);
3126 int length = Smi::cast(codes->length())->value();
3127
3128 // Check if the string can be ASCII.
3129 int i;
3130 for (i = 0; i < length; i++) {
3131 Object* element = codes->GetElement(i);
3132 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3133 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3134 break;
3135 }
3136
3137 Object* object = NULL;
3138 if (i == length) { // The string is ASCII.
3139 object = Heap::AllocateRawAsciiString(length);
3140 } else { // The string is not ASCII.
3141 object = Heap::AllocateRawTwoByteString(length);
3142 }
3143
3144 if (object->IsFailure()) return object;
3145 String* result = String::cast(object);
3146 for (int i = 0; i < length; i++) {
3147 Object* element = codes->GetElement(i);
3148 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003149 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003150 }
3151 return result;
3152}
3153
3154
3155// kNotEscaped is generated by the following:
3156//
3157// #!/bin/perl
3158// for (my $i = 0; $i < 256; $i++) {
3159// print "\n" if $i % 16 == 0;
3160// my $c = chr($i);
3161// my $escaped = 1;
3162// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3163// print $escaped ? "0, " : "1, ";
3164// }
3165
3166
3167static bool IsNotEscaped(uint16_t character) {
3168 // Only for 8 bit characters, the rest are always escaped (in a different way)
3169 ASSERT(character < 256);
3170 static const char kNotEscaped[256] = {
3171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3174 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3175 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3177 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3182 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3183 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3184 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3185 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3187 };
3188 return kNotEscaped[character] != 0;
3189}
3190
3191
3192static Object* Runtime_URIEscape(Arguments args) {
3193 const char hex_chars[] = "0123456789ABCDEF";
3194 NoHandleAllocation ha;
3195 ASSERT(args.length() == 1);
3196 CONVERT_CHECKED(String, source, args[0]);
3197
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003198 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003199
3200 int escaped_length = 0;
3201 int length = source->length();
3202 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003203 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003204 buffer->Reset(source);
3205 while (buffer->has_more()) {
3206 uint16_t character = buffer->GetNext();
3207 if (character >= 256) {
3208 escaped_length += 6;
3209 } else if (IsNotEscaped(character)) {
3210 escaped_length++;
3211 } else {
3212 escaped_length += 3;
3213 }
3214 // We don't allow strings that are longer than Smi range.
3215 if (!Smi::IsValid(escaped_length)) {
3216 Top::context()->mark_out_of_memory();
3217 return Failure::OutOfMemoryException();
3218 }
3219 }
3220 }
3221 // No length change implies no change. Return original string if no change.
3222 if (escaped_length == length) {
3223 return source;
3224 }
3225 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3226 if (o->IsFailure()) return o;
3227 String* destination = String::cast(o);
3228 int dest_position = 0;
3229
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003230 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003231 buffer->Rewind();
3232 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003233 uint16_t chr = buffer->GetNext();
3234 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003235 destination->Set(dest_position, '%');
3236 destination->Set(dest_position+1, 'u');
3237 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3238 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3239 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3240 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003242 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003243 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003244 dest_position++;
3245 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003246 destination->Set(dest_position, '%');
3247 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3248 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003249 dest_position += 3;
3250 }
3251 }
3252 return destination;
3253}
3254
3255
3256static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3257 static const signed char kHexValue['g'] = {
3258 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3259 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3260 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3261 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3262 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3263 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3264 -1, 10, 11, 12, 13, 14, 15 };
3265
3266 if (character1 > 'f') return -1;
3267 int hi = kHexValue[character1];
3268 if (hi == -1) return -1;
3269 if (character2 > 'f') return -1;
3270 int lo = kHexValue[character2];
3271 if (lo == -1) return -1;
3272 return (hi << 4) + lo;
3273}
3274
3275
ager@chromium.org870a0b62008-11-04 11:43:05 +00003276static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003277 int i,
3278 int length,
3279 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003280 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003281 int32_t hi = 0;
3282 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003283 if (character == '%' &&
3284 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003285 source->Get(i + 1) == 'u' &&
3286 (hi = TwoDigitHex(source->Get(i + 2),
3287 source->Get(i + 3))) != -1 &&
3288 (lo = TwoDigitHex(source->Get(i + 4),
3289 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003290 *step = 6;
3291 return (hi << 8) + lo;
3292 } else if (character == '%' &&
3293 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003294 (lo = TwoDigitHex(source->Get(i + 1),
3295 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003296 *step = 3;
3297 return lo;
3298 } else {
3299 *step = 1;
3300 return character;
3301 }
3302}
3303
3304
3305static Object* Runtime_URIUnescape(Arguments args) {
3306 NoHandleAllocation ha;
3307 ASSERT(args.length() == 1);
3308 CONVERT_CHECKED(String, source, args[0]);
3309
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003310 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003311
3312 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003313 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003314
3315 int unescaped_length = 0;
3316 for (int i = 0; i < length; unescaped_length++) {
3317 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003318 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003319 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003320 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003321 i += step;
3322 }
3323
3324 // No length change implies no change. Return original string if no change.
3325 if (unescaped_length == length)
3326 return source;
3327
3328 Object* o = ascii ?
3329 Heap::AllocateRawAsciiString(unescaped_length) :
3330 Heap::AllocateRawTwoByteString(unescaped_length);
3331 if (o->IsFailure()) return o;
3332 String* destination = String::cast(o);
3333
3334 int dest_position = 0;
3335 for (int i = 0; i < length; dest_position++) {
3336 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003337 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003338 i += step;
3339 }
3340 return destination;
3341}
3342
3343
3344static Object* Runtime_StringParseInt(Arguments args) {
3345 NoHandleAllocation ha;
3346
3347 CONVERT_CHECKED(String, s, args[0]);
3348 CONVERT_DOUBLE_CHECKED(n, args[1]);
3349 int radix = FastD2I(n);
3350
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003351 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003352
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003353 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003354 int i;
3355
3356 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003357 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003358 if (i == len) return Heap::nan_value();
3359
3360 // Compute the sign (default to +).
3361 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003362 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003363 sign = -1;
3364 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003365 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003366 i++;
3367 }
3368
3369 // Compute the radix if 0.
3370 if (radix == 0) {
3371 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003372 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003373 radix = 8;
3374 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003375 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003376 if (c == 'x' || c == 'X') {
3377 radix = 16;
3378 i += 2;
3379 }
3380 }
3381 }
3382 } else if (radix == 16) {
3383 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003384 if (i + 1 < len && s->Get(i) == '0') {
3385 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003386 if (c == 'x' || c == 'X') i += 2;
3387 }
3388 }
3389
3390 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3391 double value;
3392 int end_index = StringToInt(s, i, radix, &value);
3393 if (end_index != i) {
3394 return Heap::NumberFromDouble(sign * value);
3395 }
3396 return Heap::nan_value();
3397}
3398
3399
3400static Object* Runtime_StringParseFloat(Arguments args) {
3401 NoHandleAllocation ha;
3402 CONVERT_CHECKED(String, str, args[0]);
3403
3404 // ECMA-262 section 15.1.2.3, empty string is NaN
3405 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3406
3407 // Create a number object from the value.
3408 return Heap::NumberFromDouble(value);
3409}
3410
3411
3412static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3413static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3414
3415
3416template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003417static Object* ConvertCaseHelper(String* s,
3418 int length,
3419 int input_string_length,
3420 unibrow::Mapping<Converter, 128>* mapping) {
3421 // We try this twice, once with the assumption that the result is no longer
3422 // than the input and, if that assumption breaks, again with the exact
3423 // length. This may not be pretty, but it is nicer than what was here before
3424 // and I hereby claim my vaffel-is.
3425 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003426 // Allocate the resulting string.
3427 //
3428 // NOTE: This assumes that the upper/lower case of an ascii
3429 // character is also ascii. This is currently the case, but it
3430 // might break in the future if we implement more context and locale
3431 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003432 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003433 ? Heap::AllocateRawAsciiString(length)
3434 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003435 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003436 String* result = String::cast(o);
3437 bool has_changed_character = false;
3438
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003439 // Convert all characters to upper case, assuming that they will fit
3440 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003441 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003442 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003443 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003444 // We can assume that the string is not empty
3445 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003446 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003447 bool has_next = buffer->has_more();
3448 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003449 int char_length = mapping->get(current, next, chars);
3450 if (char_length == 0) {
3451 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003452 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003453 i++;
3454 } else if (char_length == 1) {
3455 // Common case: converting the letter resulted in one character.
3456 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003457 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003458 has_changed_character = true;
3459 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003460 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003461 // We've assumed that the result would be as long as the
3462 // input but here is a character that converts to several
3463 // characters. No matter, we calculate the exact length
3464 // of the result and try the whole thing again.
3465 //
3466 // Note that this leaves room for optimization. We could just
3467 // memcpy what we already have to the result string. Also,
3468 // the result string is the last object allocated we could
3469 // "realloc" it and probably, in the vast majority of cases,
3470 // extend the existing string to be able to hold the full
3471 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003472 int next_length = 0;
3473 if (has_next) {
3474 next_length = mapping->get(next, 0, chars);
3475 if (next_length == 0) next_length = 1;
3476 }
3477 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003478 while (buffer->has_more()) {
3479 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003480 // NOTE: we use 0 as the next character here because, while
3481 // the next character may affect what a character converts to,
3482 // it does not in any case affect the length of what it convert
3483 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003484 int char_length = mapping->get(current, 0, chars);
3485 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003486 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003487 if (current_length > Smi::kMaxValue) {
3488 Top::context()->mark_out_of_memory();
3489 return Failure::OutOfMemoryException();
3490 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003491 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003492 // Try again with the real length.
3493 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003494 } else {
3495 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003496 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003497 i++;
3498 }
3499 has_changed_character = true;
3500 }
3501 current = next;
3502 }
3503 if (has_changed_character) {
3504 return result;
3505 } else {
3506 // If we didn't actually change anything in doing the conversion
3507 // we simple return the result and let the converted string
3508 // become garbage; there is no reason to keep two identical strings
3509 // alive.
3510 return s;
3511 }
3512}
3513
3514
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003515template <class Converter>
3516static Object* ConvertCase(Arguments args,
3517 unibrow::Mapping<Converter, 128>* mapping) {
3518 NoHandleAllocation ha;
3519
3520 CONVERT_CHECKED(String, s, args[0]);
3521 s->TryFlattenIfNotFlat();
3522
3523 int input_string_length = s->length();
3524 // Assume that the string is not empty; we need this assumption later
3525 if (input_string_length == 0) return s;
3526 int length = input_string_length;
3527
3528 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3529 if (answer->IsSmi()) {
3530 // Retry with correct length.
3531 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3532 }
3533 return answer; // This may be a failure.
3534}
3535
3536
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003537static Object* Runtime_StringToLowerCase(Arguments args) {
3538 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3539}
3540
3541
3542static Object* Runtime_StringToUpperCase(Arguments args) {
3543 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3544}
3545
3546
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003547bool Runtime::IsUpperCaseChar(uint16_t ch) {
3548 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3549 int char_length = to_upper_mapping.get(ch, 0, chars);
3550 return char_length == 0;
3551}
3552
3553
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003554static Object* Runtime_NumberToString(Arguments args) {
3555 NoHandleAllocation ha;
3556 ASSERT(args.length() == 1);
3557
3558 Object* number = args[0];
3559 RUNTIME_ASSERT(number->IsNumber());
3560
3561 Object* cached = Heap::GetNumberStringCache(number);
3562 if (cached != Heap::undefined_value()) {
3563 return cached;
3564 }
3565
3566 char arr[100];
3567 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3568 const char* str;
3569 if (number->IsSmi()) {
3570 int num = Smi::cast(number)->value();
3571 str = IntToCString(num, buffer);
3572 } else {
3573 double num = HeapNumber::cast(number)->value();
3574 str = DoubleToCString(num, buffer);
3575 }
3576 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3577
3578 if (!result->IsFailure()) {
3579 Heap::SetNumberStringCache(number, String::cast(result));
3580 }
3581 return result;
3582}
3583
3584
3585static Object* Runtime_NumberToInteger(Arguments args) {
3586 NoHandleAllocation ha;
3587 ASSERT(args.length() == 1);
3588
3589 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003590 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003591 CONVERT_DOUBLE_CHECKED(number, obj);
3592 return Heap::NumberFromDouble(DoubleToInteger(number));
3593}
3594
3595
3596static Object* Runtime_NumberToJSUint32(Arguments args) {
3597 NoHandleAllocation ha;
3598 ASSERT(args.length() == 1);
3599
3600 Object* obj = args[0];
3601 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3602 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3603 return Heap::NumberFromUint32(number);
3604}
3605
3606
3607static Object* Runtime_NumberToJSInt32(Arguments args) {
3608 NoHandleAllocation ha;
3609 ASSERT(args.length() == 1);
3610
3611 Object* obj = args[0];
3612 if (obj->IsSmi()) return obj;
3613 CONVERT_DOUBLE_CHECKED(number, obj);
3614 return Heap::NumberFromInt32(DoubleToInt32(number));
3615}
3616
3617
ager@chromium.org870a0b62008-11-04 11:43:05 +00003618// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3619// a small integer.
3620static Object* Runtime_NumberToSmi(Arguments args) {
3621 NoHandleAllocation ha;
3622 ASSERT(args.length() == 1);
3623
3624 Object* obj = args[0];
3625 if (obj->IsSmi()) {
3626 return obj;
3627 }
3628 if (obj->IsHeapNumber()) {
3629 double value = HeapNumber::cast(obj)->value();
3630 int int_value = FastD2I(value);
3631 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3632 return Smi::FromInt(int_value);
3633 }
3634 }
3635 return Heap::nan_value();
3636}
3637
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003638
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003639static Object* Runtime_NumberAdd(Arguments args) {
3640 NoHandleAllocation ha;
3641 ASSERT(args.length() == 2);
3642
3643 CONVERT_DOUBLE_CHECKED(x, args[0]);
3644 CONVERT_DOUBLE_CHECKED(y, args[1]);
3645 return Heap::AllocateHeapNumber(x + y);
3646}
3647
3648
3649static Object* Runtime_NumberSub(Arguments args) {
3650 NoHandleAllocation ha;
3651 ASSERT(args.length() == 2);
3652
3653 CONVERT_DOUBLE_CHECKED(x, args[0]);
3654 CONVERT_DOUBLE_CHECKED(y, args[1]);
3655 return Heap::AllocateHeapNumber(x - y);
3656}
3657
3658
3659static Object* Runtime_NumberMul(Arguments args) {
3660 NoHandleAllocation ha;
3661 ASSERT(args.length() == 2);
3662
3663 CONVERT_DOUBLE_CHECKED(x, args[0]);
3664 CONVERT_DOUBLE_CHECKED(y, args[1]);
3665 return Heap::AllocateHeapNumber(x * y);
3666}
3667
3668
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003669static Object* Runtime_NumberUnaryMinus(Arguments args) {
3670 NoHandleAllocation ha;
3671 ASSERT(args.length() == 1);
3672
3673 CONVERT_DOUBLE_CHECKED(x, args[0]);
3674 return Heap::AllocateHeapNumber(-x);
3675}
3676
3677
3678static Object* Runtime_NumberDiv(Arguments args) {
3679 NoHandleAllocation ha;
3680 ASSERT(args.length() == 2);
3681
3682 CONVERT_DOUBLE_CHECKED(x, args[0]);
3683 CONVERT_DOUBLE_CHECKED(y, args[1]);
3684 return Heap::NewNumberFromDouble(x / y);
3685}
3686
3687
3688static Object* Runtime_NumberMod(Arguments args) {
3689 NoHandleAllocation ha;
3690 ASSERT(args.length() == 2);
3691
3692 CONVERT_DOUBLE_CHECKED(x, args[0]);
3693 CONVERT_DOUBLE_CHECKED(y, args[1]);
3694
3695#ifdef WIN32
3696 // Workaround MS fmod bugs. ECMA-262 says:
3697 // dividend is finite and divisor is an infinity => result equals dividend
3698 // dividend is a zero and divisor is nonzero finite => result equals dividend
3699 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3700 !(x == 0 && (y != 0 && isfinite(y))))
3701#endif
3702 x = fmod(x, y);
3703 // NewNumberFromDouble may return a Smi instead of a Number object
3704 return Heap::NewNumberFromDouble(x);
3705}
3706
3707
3708static Object* Runtime_StringAdd(Arguments args) {
3709 NoHandleAllocation ha;
3710 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003711 CONVERT_CHECKED(String, str1, args[0]);
3712 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003713 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003714}
3715
3716
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003717template<typename sinkchar>
3718static inline void StringBuilderConcatHelper(String* special,
3719 sinkchar* sink,
3720 FixedArray* fixed_array,
3721 int array_length) {
3722 int position = 0;
3723 for (int i = 0; i < array_length; i++) {
3724 Object* element = fixed_array->get(i);
3725 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003726 int encoded_slice = Smi::cast(element)->value();
3727 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3728 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003729 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003730 sink + position,
3731 pos,
3732 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003733 position += len;
3734 } else {
3735 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003736 int element_length = string->length();
3737 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003738 position += element_length;
3739 }
3740 }
3741}
3742
3743
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003744static Object* Runtime_StringBuilderConcat(Arguments args) {
3745 NoHandleAllocation ha;
3746 ASSERT(args.length() == 2);
3747 CONVERT_CHECKED(JSArray, array, args[0]);
3748 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003749 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003750 Object* smi_array_length = array->length();
3751 if (!smi_array_length->IsSmi()) {
3752 Top::context()->mark_out_of_memory();
3753 return Failure::OutOfMemoryException();
3754 }
3755 int array_length = Smi::cast(smi_array_length)->value();
3756 if (!array->HasFastElements()) {
3757 return Top::Throw(Heap::illegal_argument_symbol());
3758 }
3759 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003760 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003761 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003762 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003763
3764 if (array_length == 0) {
3765 return Heap::empty_string();
3766 } else if (array_length == 1) {
3767 Object* first = fixed_array->get(0);
3768 if (first->IsString()) return first;
3769 }
3770
ager@chromium.org5ec48922009-05-05 07:25:34 +00003771 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003772 int position = 0;
3773 for (int i = 0; i < array_length; i++) {
3774 Object* elt = fixed_array->get(i);
3775 if (elt->IsSmi()) {
3776 int len = Smi::cast(elt)->value();
3777 int pos = len >> 11;
3778 len &= 0x7ff;
3779 if (pos + len > special_length) {
3780 return Top::Throw(Heap::illegal_argument_symbol());
3781 }
3782 position += len;
3783 } else if (elt->IsString()) {
3784 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003785 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003786 if (!Smi::IsValid(element_length + position)) {
3787 Top::context()->mark_out_of_memory();
3788 return Failure::OutOfMemoryException();
3789 }
3790 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003791 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003792 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003793 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003794 } else {
3795 return Top::Throw(Heap::illegal_argument_symbol());
3796 }
3797 }
3798
3799 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003800 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003801
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003802 if (ascii) {
3803 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003804 if (object->IsFailure()) return object;
3805 SeqAsciiString* answer = SeqAsciiString::cast(object);
3806 StringBuilderConcatHelper(special,
3807 answer->GetChars(),
3808 fixed_array,
3809 array_length);
3810 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003811 } else {
3812 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003813 if (object->IsFailure()) return object;
3814 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3815 StringBuilderConcatHelper(special,
3816 answer->GetChars(),
3817 fixed_array,
3818 array_length);
3819 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003820 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003821}
3822
3823
3824static Object* Runtime_NumberOr(Arguments args) {
3825 NoHandleAllocation ha;
3826 ASSERT(args.length() == 2);
3827
3828 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3829 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3830 return Heap::NumberFromInt32(x | y);
3831}
3832
3833
3834static Object* Runtime_NumberAnd(Arguments args) {
3835 NoHandleAllocation ha;
3836 ASSERT(args.length() == 2);
3837
3838 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3839 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3840 return Heap::NumberFromInt32(x & y);
3841}
3842
3843
3844static Object* Runtime_NumberXor(Arguments args) {
3845 NoHandleAllocation ha;
3846 ASSERT(args.length() == 2);
3847
3848 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3849 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3850 return Heap::NumberFromInt32(x ^ y);
3851}
3852
3853
3854static Object* Runtime_NumberNot(Arguments args) {
3855 NoHandleAllocation ha;
3856 ASSERT(args.length() == 1);
3857
3858 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3859 return Heap::NumberFromInt32(~x);
3860}
3861
3862
3863static Object* Runtime_NumberShl(Arguments args) {
3864 NoHandleAllocation ha;
3865 ASSERT(args.length() == 2);
3866
3867 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3868 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3869 return Heap::NumberFromInt32(x << (y & 0x1f));
3870}
3871
3872
3873static Object* Runtime_NumberShr(Arguments args) {
3874 NoHandleAllocation ha;
3875 ASSERT(args.length() == 2);
3876
3877 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3878 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3879 return Heap::NumberFromUint32(x >> (y & 0x1f));
3880}
3881
3882
3883static Object* Runtime_NumberSar(Arguments args) {
3884 NoHandleAllocation ha;
3885 ASSERT(args.length() == 2);
3886
3887 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3888 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3889 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3890}
3891
3892
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003893static Object* Runtime_NumberEquals(Arguments args) {
3894 NoHandleAllocation ha;
3895 ASSERT(args.length() == 2);
3896
3897 CONVERT_DOUBLE_CHECKED(x, args[0]);
3898 CONVERT_DOUBLE_CHECKED(y, args[1]);
3899 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3900 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3901 if (x == y) return Smi::FromInt(EQUAL);
3902 Object* result;
3903 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3904 result = Smi::FromInt(EQUAL);
3905 } else {
3906 result = Smi::FromInt(NOT_EQUAL);
3907 }
3908 return result;
3909}
3910
3911
3912static Object* Runtime_StringEquals(Arguments args) {
3913 NoHandleAllocation ha;
3914 ASSERT(args.length() == 2);
3915
3916 CONVERT_CHECKED(String, x, args[0]);
3917 CONVERT_CHECKED(String, y, args[1]);
3918
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003919 bool not_equal = !x->Equals(y);
3920 // This is slightly convoluted because the value that signifies
3921 // equality is 0 and inequality is 1 so we have to negate the result
3922 // from String::Equals.
3923 ASSERT(not_equal == 0 || not_equal == 1);
3924 STATIC_CHECK(EQUAL == 0);
3925 STATIC_CHECK(NOT_EQUAL == 1);
3926 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927}
3928
3929
3930static Object* Runtime_NumberCompare(Arguments args) {
3931 NoHandleAllocation ha;
3932 ASSERT(args.length() == 3);
3933
3934 CONVERT_DOUBLE_CHECKED(x, args[0]);
3935 CONVERT_DOUBLE_CHECKED(y, args[1]);
3936 if (isnan(x) || isnan(y)) return args[2];
3937 if (x == y) return Smi::FromInt(EQUAL);
3938 if (isless(x, y)) return Smi::FromInt(LESS);
3939 return Smi::FromInt(GREATER);
3940}
3941
3942
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003943// Compare two Smis as if they were converted to strings and then
3944// compared lexicographically.
3945static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3946 NoHandleAllocation ha;
3947 ASSERT(args.length() == 2);
3948
3949 // Arrays for the individual characters of the two Smis. Smis are
3950 // 31 bit integers and 10 decimal digits are therefore enough.
3951 static int x_elms[10];
3952 static int y_elms[10];
3953
3954 // Extract the integer values from the Smis.
3955 CONVERT_CHECKED(Smi, x, args[0]);
3956 CONVERT_CHECKED(Smi, y, args[1]);
3957 int x_value = x->value();
3958 int y_value = y->value();
3959
3960 // If the integers are equal so are the string representations.
3961 if (x_value == y_value) return Smi::FromInt(EQUAL);
3962
3963 // If one of the integers are zero the normal integer order is the
3964 // same as the lexicographic order of the string representations.
3965 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3966
ager@chromium.org32912102009-01-16 10:38:43 +00003967 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003968 // smallest because the char code of '-' is less than the char code
3969 // of any digit. Otherwise, we make both values positive.
3970 if (x_value < 0 || y_value < 0) {
3971 if (y_value >= 0) return Smi::FromInt(LESS);
3972 if (x_value >= 0) return Smi::FromInt(GREATER);
3973 x_value = -x_value;
3974 y_value = -y_value;
3975 }
3976
3977 // Convert the integers to arrays of their decimal digits.
3978 int x_index = 0;
3979 int y_index = 0;
3980 while (x_value > 0) {
3981 x_elms[x_index++] = x_value % 10;
3982 x_value /= 10;
3983 }
3984 while (y_value > 0) {
3985 y_elms[y_index++] = y_value % 10;
3986 y_value /= 10;
3987 }
3988
3989 // Loop through the arrays of decimal digits finding the first place
3990 // where they differ.
3991 while (--x_index >= 0 && --y_index >= 0) {
3992 int diff = x_elms[x_index] - y_elms[y_index];
3993 if (diff != 0) return Smi::FromInt(diff);
3994 }
3995
3996 // If one array is a suffix of the other array, the longest array is
3997 // the representation of the largest of the Smis in the
3998 // lexicographic ordering.
3999 return Smi::FromInt(x_index - y_index);
4000}
4001
4002
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004003static Object* Runtime_StringCompare(Arguments args) {
4004 NoHandleAllocation ha;
4005 ASSERT(args.length() == 2);
4006
4007 CONVERT_CHECKED(String, x, args[0]);
4008 CONVERT_CHECKED(String, y, args[1]);
4009
4010 // A few fast case tests before we flatten.
4011 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004012 if (y->length() == 0) {
4013 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004014 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004015 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004016 return Smi::FromInt(LESS);
4017 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004018
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004019 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004020 if (d < 0) return Smi::FromInt(LESS);
4021 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004022
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004023 x->TryFlattenIfNotFlat();
4024 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004025
4026 static StringInputBuffer bufx;
4027 static StringInputBuffer bufy;
4028 bufx.Reset(x);
4029 bufy.Reset(y);
4030 while (bufx.has_more() && bufy.has_more()) {
4031 int d = bufx.GetNext() - bufy.GetNext();
4032 if (d < 0) return Smi::FromInt(LESS);
4033 else if (d > 0) return Smi::FromInt(GREATER);
4034 }
4035
4036 // x is (non-trivial) prefix of y:
4037 if (bufy.has_more()) return Smi::FromInt(LESS);
4038 // y is prefix of x:
4039 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4040}
4041
4042
4043static Object* Runtime_Math_abs(Arguments args) {
4044 NoHandleAllocation ha;
4045 ASSERT(args.length() == 1);
4046
4047 CONVERT_DOUBLE_CHECKED(x, args[0]);
4048 return Heap::AllocateHeapNumber(fabs(x));
4049}
4050
4051
4052static Object* Runtime_Math_acos(Arguments args) {
4053 NoHandleAllocation ha;
4054 ASSERT(args.length() == 1);
4055
4056 CONVERT_DOUBLE_CHECKED(x, args[0]);
4057 return Heap::AllocateHeapNumber(acos(x));
4058}
4059
4060
4061static Object* Runtime_Math_asin(Arguments args) {
4062 NoHandleAllocation ha;
4063 ASSERT(args.length() == 1);
4064
4065 CONVERT_DOUBLE_CHECKED(x, args[0]);
4066 return Heap::AllocateHeapNumber(asin(x));
4067}
4068
4069
4070static Object* Runtime_Math_atan(Arguments args) {
4071 NoHandleAllocation ha;
4072 ASSERT(args.length() == 1);
4073
4074 CONVERT_DOUBLE_CHECKED(x, args[0]);
4075 return Heap::AllocateHeapNumber(atan(x));
4076}
4077
4078
4079static Object* Runtime_Math_atan2(Arguments args) {
4080 NoHandleAllocation ha;
4081 ASSERT(args.length() == 2);
4082
4083 CONVERT_DOUBLE_CHECKED(x, args[0]);
4084 CONVERT_DOUBLE_CHECKED(y, args[1]);
4085 double result;
4086 if (isinf(x) && isinf(y)) {
4087 // Make sure that the result in case of two infinite arguments
4088 // is a multiple of Pi / 4. The sign of the result is determined
4089 // by the first argument (x) and the sign of the second argument
4090 // determines the multiplier: one or three.
4091 static double kPiDividedBy4 = 0.78539816339744830962;
4092 int multiplier = (x < 0) ? -1 : 1;
4093 if (y < 0) multiplier *= 3;
4094 result = multiplier * kPiDividedBy4;
4095 } else {
4096 result = atan2(x, y);
4097 }
4098 return Heap::AllocateHeapNumber(result);
4099}
4100
4101
4102static Object* Runtime_Math_ceil(Arguments args) {
4103 NoHandleAllocation ha;
4104 ASSERT(args.length() == 1);
4105
4106 CONVERT_DOUBLE_CHECKED(x, args[0]);
4107 return Heap::NumberFromDouble(ceiling(x));
4108}
4109
4110
4111static Object* Runtime_Math_cos(Arguments args) {
4112 NoHandleAllocation ha;
4113 ASSERT(args.length() == 1);
4114
4115 CONVERT_DOUBLE_CHECKED(x, args[0]);
4116 return Heap::AllocateHeapNumber(cos(x));
4117}
4118
4119
4120static Object* Runtime_Math_exp(Arguments args) {
4121 NoHandleAllocation ha;
4122 ASSERT(args.length() == 1);
4123
4124 CONVERT_DOUBLE_CHECKED(x, args[0]);
4125 return Heap::AllocateHeapNumber(exp(x));
4126}
4127
4128
4129static Object* Runtime_Math_floor(Arguments args) {
4130 NoHandleAllocation ha;
4131 ASSERT(args.length() == 1);
4132
4133 CONVERT_DOUBLE_CHECKED(x, args[0]);
4134 return Heap::NumberFromDouble(floor(x));
4135}
4136
4137
4138static Object* Runtime_Math_log(Arguments args) {
4139 NoHandleAllocation ha;
4140 ASSERT(args.length() == 1);
4141
4142 CONVERT_DOUBLE_CHECKED(x, args[0]);
4143 return Heap::AllocateHeapNumber(log(x));
4144}
4145
4146
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004147// Helper function to compute x^y, where y is known to be an
4148// integer. Uses binary decomposition to limit the number of
4149// multiplications; see the discussion in "Hacker's Delight" by Henry
4150// S. Warren, Jr., figure 11-6, page 213.
4151static double powi(double x, int y) {
4152 ASSERT(y != kMinInt);
4153 unsigned n = (y < 0) ? -y : y;
4154 double m = x;
4155 double p = 1;
4156 while (true) {
4157 if ((n & 1) != 0) p *= m;
4158 n >>= 1;
4159 if (n == 0) {
4160 if (y < 0) {
4161 // Unfortunately, we have to be careful when p has reached
4162 // infinity in the computation, because sometimes the higher
4163 // internal precision in the pow() implementation would have
4164 // given us a finite p. This happens very rarely.
4165 double result = 1.0 / p;
4166 return (result == 0 && isinf(p))
4167 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4168 : result;
4169 } else {
4170 return p;
4171 }
4172 }
4173 m *= m;
4174 }
4175}
4176
4177
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004178static Object* Runtime_Math_pow(Arguments args) {
4179 NoHandleAllocation ha;
4180 ASSERT(args.length() == 2);
4181
4182 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004183
4184 // If the second argument is a smi, it is much faster to call the
4185 // custom powi() function than the generic pow().
4186 if (args[1]->IsSmi()) {
4187 int y = Smi::cast(args[1])->value();
4188 return Heap::AllocateHeapNumber(powi(x, y));
4189 }
4190
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004191 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004192
4193 if (!isinf(x)) {
4194 if (y == 0.5) {
4195 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4196 // square root of a number. To speed up such computations, we
4197 // explictly check for this case and use the sqrt() function
4198 // which is faster than pow().
4199 return Heap::AllocateHeapNumber(sqrt(x));
4200 } else if (y == -0.5) {
4201 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4202 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4203 }
4204 }
4205
4206 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004207 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004208 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4209 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004210 } else {
4211 return Heap::AllocateHeapNumber(pow(x, y));
4212 }
4213}
4214
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004215
4216static Object* Runtime_Math_round(Arguments args) {
4217 NoHandleAllocation ha;
4218 ASSERT(args.length() == 1);
4219
4220 CONVERT_DOUBLE_CHECKED(x, args[0]);
4221 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4222 return Heap::NumberFromDouble(floor(x + 0.5));
4223}
4224
4225
4226static Object* Runtime_Math_sin(Arguments args) {
4227 NoHandleAllocation ha;
4228 ASSERT(args.length() == 1);
4229
4230 CONVERT_DOUBLE_CHECKED(x, args[0]);
4231 return Heap::AllocateHeapNumber(sin(x));
4232}
4233
4234
4235static Object* Runtime_Math_sqrt(Arguments args) {
4236 NoHandleAllocation ha;
4237 ASSERT(args.length() == 1);
4238
4239 CONVERT_DOUBLE_CHECKED(x, args[0]);
4240 return Heap::AllocateHeapNumber(sqrt(x));
4241}
4242
4243
4244static Object* Runtime_Math_tan(Arguments args) {
4245 NoHandleAllocation ha;
4246 ASSERT(args.length() == 1);
4247
4248 CONVERT_DOUBLE_CHECKED(x, args[0]);
4249 return Heap::AllocateHeapNumber(tan(x));
4250}
4251
4252
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004253// The NewArguments function is only used when constructing the
4254// arguments array when calling non-functions from JavaScript in
4255// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004256static Object* Runtime_NewArguments(Arguments args) {
4257 NoHandleAllocation ha;
4258 ASSERT(args.length() == 1);
4259
4260 // ECMA-262, 3rd., 10.1.8, p.39
4261 CONVERT_CHECKED(JSFunction, callee, args[0]);
4262
4263 // Compute the frame holding the arguments.
4264 JavaScriptFrameIterator it;
4265 it.AdvanceToArgumentsFrame();
4266 JavaScriptFrame* frame = it.frame();
4267
4268 const int length = frame->GetProvidedParametersCount();
4269 Object* result = Heap::AllocateArgumentsObject(callee, length);
4270 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004271 if (length > 0) {
4272 Object* obj = Heap::AllocateFixedArray(length);
4273 if (obj->IsFailure()) return obj;
4274 FixedArray* array = FixedArray::cast(obj);
4275 ASSERT(array->length() == length);
4276 WriteBarrierMode mode = array->GetWriteBarrierMode();
4277 for (int i = 0; i < length; i++) {
4278 array->set(i, frame->GetParameter(i), mode);
4279 }
4280 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004281 }
4282 return result;
4283}
4284
4285
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004286static Object* Runtime_NewArgumentsFast(Arguments args) {
4287 NoHandleAllocation ha;
4288 ASSERT(args.length() == 3);
4289
4290 JSFunction* callee = JSFunction::cast(args[0]);
4291 Object** parameters = reinterpret_cast<Object**>(args[1]);
4292 const int length = Smi::cast(args[2])->value();
4293
4294 Object* result = Heap::AllocateArgumentsObject(callee, length);
4295 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004296 ASSERT(Heap::InNewSpace(result));
4297
4298 // Allocate the elements if needed.
4299 if (length > 0) {
4300 // Allocate the fixed array.
4301 Object* obj = Heap::AllocateRawFixedArray(length);
4302 if (obj->IsFailure()) return obj;
4303 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4304 FixedArray* array = FixedArray::cast(obj);
4305 array->set_length(length);
4306 WriteBarrierMode mode = array->GetWriteBarrierMode();
4307 for (int i = 0; i < length; i++) {
4308 array->set(i, *--parameters, mode);
4309 }
4310 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4311 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004312 }
4313 return result;
4314}
4315
4316
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004317static Object* Runtime_NewClosure(Arguments args) {
4318 HandleScope scope;
4319 ASSERT(args.length() == 2);
4320 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4321 CONVERT_ARG_CHECKED(Context, context, 1);
4322
4323 Handle<JSFunction> result =
4324 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4325 return *result;
4326}
4327
4328
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004329static Handle<Code> ComputeConstructStub(Handle<Map> map) {
4330 // TODO(385): Change this to create a construct stub specialized for
4331 // the given map to make allocation of simple objects - and maybe
4332 // arrays - much faster.
4333 return Handle<Code>(Builtins::builtin(Builtins::JSConstructStubGeneric));
4334}
4335
4336
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004337static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004338 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004339 ASSERT(args.length() == 1);
4340
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004341 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004342
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004343 // If the constructor isn't a proper function we throw a type error.
4344 if (!constructor->IsJSFunction()) {
4345 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4346 Handle<Object> type_error =
4347 Factory::NewTypeError("not_constructor", arguments);
4348 return Top::Throw(*type_error);
4349 }
4350
4351 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004352#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004353 // Handle stepping into constructors if step into is active.
4354 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004355 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004356 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004357#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004358
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004359 if (function->has_initial_map()) {
4360 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004361 // The 'Function' function ignores the receiver object when
4362 // called using 'new' and creates a new JSFunction object that
4363 // is returned. The receiver object is only used for error
4364 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004365 // JSFunction. Factory::NewJSObject() should not be used to
4366 // allocate JSFunctions since it does not properly initialize
4367 // the shared part of the function. Since the receiver is
4368 // ignored anyway, we use the global object as the receiver
4369 // instead of a new JSFunction object. This way, errors are
4370 // reported the same way whether or not 'Function' is called
4371 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004372 return Top::context()->global();
4373 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004374 }
4375
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004376 bool first_allocation = !function->has_initial_map();
4377 Handle<JSObject> result = Factory::NewJSObject(function);
4378 if (first_allocation) {
4379 Handle<Map> map = Handle<Map>(function->initial_map());
4380 Handle<Code> stub = ComputeConstructStub(map);
4381 function->shared()->set_construct_stub(*stub);
4382 }
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004383 Counters::constructed_objects.Increment();
4384 Counters::constructed_objects_runtime.Increment();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004385 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004386}
4387
4388
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004389static Object* Runtime_LazyCompile(Arguments args) {
4390 HandleScope scope;
4391 ASSERT(args.length() == 1);
4392
4393 Handle<JSFunction> function = args.at<JSFunction>(0);
4394#ifdef DEBUG
4395 if (FLAG_trace_lazy) {
4396 PrintF("[lazy: ");
4397 function->shared()->name()->Print();
4398 PrintF("]\n");
4399 }
4400#endif
4401
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004402 // Compile the target function. Here we compile using CompileLazyInLoop in
4403 // order to get the optimized version. This helps code like delta-blue
4404 // that calls performance-critical routines through constructors. A
4405 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4406 // direct call. Since the in-loop tracking takes place through CallICs
4407 // this means that things called through constructors are never known to
4408 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004409 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004410 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004411 return Failure::Exception();
4412 }
4413
4414 return function->code();
4415}
4416
4417
4418static Object* Runtime_GetCalledFunction(Arguments args) {
4419 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004420 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004421 StackFrameIterator it;
4422 // Get past the JS-to-C exit frame.
4423 ASSERT(it.frame()->is_exit());
4424 it.Advance();
4425 // Get past the CALL_NON_FUNCTION activation frame.
4426 ASSERT(it.frame()->is_java_script());
4427 it.Advance();
4428 // Argument adaptor frames do not copy the function; we have to skip
4429 // past them to get to the real calling frame.
4430 if (it.frame()->is_arguments_adaptor()) it.Advance();
4431 // Get the function from the top of the expression stack of the
4432 // calling frame.
4433 StandardFrame* frame = StandardFrame::cast(it.frame());
4434 int index = frame->ComputeExpressionsCount() - 1;
4435 Object* result = frame->GetExpression(index);
4436 return result;
4437}
4438
4439
4440static Object* Runtime_GetFunctionDelegate(Arguments args) {
4441 HandleScope scope;
4442 ASSERT(args.length() == 1);
4443 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4444 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4445}
4446
4447
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004448static Object* Runtime_GetConstructorDelegate(Arguments args) {
4449 HandleScope scope;
4450 ASSERT(args.length() == 1);
4451 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4452 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4453}
4454
4455
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004456static Object* Runtime_NewContext(Arguments args) {
4457 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004458 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004459
kasper.lund7276f142008-07-30 08:49:36 +00004460 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004461 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4462 Object* result = Heap::AllocateFunctionContext(length, function);
4463 if (result->IsFailure()) return result;
4464
4465 Top::set_context(Context::cast(result));
4466
kasper.lund7276f142008-07-30 08:49:36 +00004467 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004468}
4469
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004470static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004471 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004472 Object* js_object = object;
4473 if (!js_object->IsJSObject()) {
4474 js_object = js_object->ToObject();
4475 if (js_object->IsFailure()) {
4476 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004477 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004478 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004479 Handle<Object> result =
4480 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4481 return Top::Throw(*result);
4482 }
4483 }
4484
4485 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004486 Heap::AllocateWithContext(Top::context(),
4487 JSObject::cast(js_object),
4488 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004489 if (result->IsFailure()) return result;
4490
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004491 Context* context = Context::cast(result);
4492 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004493
kasper.lund7276f142008-07-30 08:49:36 +00004494 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004495}
4496
4497
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004498static Object* Runtime_PushContext(Arguments args) {
4499 NoHandleAllocation ha;
4500 ASSERT(args.length() == 1);
4501 return PushContextHelper(args[0], false);
4502}
4503
4504
4505static Object* Runtime_PushCatchContext(Arguments args) {
4506 NoHandleAllocation ha;
4507 ASSERT(args.length() == 1);
4508 return PushContextHelper(args[0], true);
4509}
4510
4511
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004512static Object* Runtime_LookupContext(Arguments args) {
4513 HandleScope scope;
4514 ASSERT(args.length() == 2);
4515
4516 CONVERT_ARG_CHECKED(Context, context, 0);
4517 CONVERT_ARG_CHECKED(String, name, 1);
4518
4519 int index;
4520 PropertyAttributes attributes;
4521 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004522 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004523 context->Lookup(name, flags, &index, &attributes);
4524
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004525 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004526 ASSERT(holder->IsJSObject());
4527 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004528 }
4529
4530 // No intermediate context found. Use global object by default.
4531 return Top::context()->global();
4532}
4533
4534
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004535// A mechanism to return pairs of Object*'s. This is somewhat
4536// compiler-dependent as it assumes that a 64-bit value (a long long)
4537// is returned via two registers (edx:eax on ia32). Both the ia32 and
4538// arm platform support this; it is mostly an issue of "coaxing" the
4539// compiler to do the right thing.
4540//
4541// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004542#ifdef V8_HOST_ARCH_64_BIT
4543// Tested with GCC, not with MSVC.
4544struct ObjectPair {
4545 Object* x;
4546 Object* y;
4547};
4548static inline ObjectPair MakePair(Object* x, Object* y) {
4549 ObjectPair result = {x, y};
4550 return result; // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4551}
4552#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004553typedef uint64_t ObjectPair;
4554static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004555 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004556 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004557}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004558#endif
4559
4560
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004561
4562
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004563static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004564 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4565 USE(attributes);
4566 return x->IsTheHole() ? Heap::undefined_value() : x;
4567}
4568
4569
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004570static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4571 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004572 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004573 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004574 JSFunction* context_extension_function =
4575 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004576 // If the holder isn't a context extension object, we just return it
4577 // as the receiver. This allows arguments objects to be used as
4578 // receivers, but only if they are put in the context scope chain
4579 // explicitly via a with-statement.
4580 Object* constructor = holder->map()->constructor();
4581 if (constructor != context_extension_function) return holder;
4582 // Fall back to using the global object as the receiver if the
4583 // property turns out to be a local variable allocated in a context
4584 // extension object - introduced via eval.
4585 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004586}
4587
4588
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004589static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004590 HandleScope scope;
4591 ASSERT(args.length() == 2);
4592
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004593 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004594 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004595 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004596 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004597 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004598
4599 int index;
4600 PropertyAttributes attributes;
4601 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004602 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004603 context->Lookup(name, flags, &index, &attributes);
4604
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004605 // If the index is non-negative, the slot has been found in a local
4606 // variable or a parameter. Read it from the context object or the
4607 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004608 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004609 // If the "property" we were looking for is a local variable or an
4610 // argument in a context, the receiver is the global object; see
4611 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4612 JSObject* receiver = Top::context()->global()->global_receiver();
4613 Object* value = (holder->IsContext())
4614 ? Context::cast(*holder)->get(index)
4615 : JSObject::cast(*holder)->GetElement(index);
4616 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004617 }
4618
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004619 // If the holder is found, we read the property from it.
4620 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004621 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004622 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004623 JSObject* receiver;
4624 if (object->IsGlobalObject()) {
4625 receiver = GlobalObject::cast(object)->global_receiver();
4626 } else if (context->is_exception_holder(*holder)) {
4627 receiver = Top::context()->global()->global_receiver();
4628 } else {
4629 receiver = ComputeReceiverForNonGlobal(object);
4630 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004631 // No need to unhole the value here. This is taken care of by the
4632 // GetProperty function.
4633 Object* value = object->GetProperty(*name);
4634 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004635 }
4636
4637 if (throw_error) {
4638 // The property doesn't exist - throw exception.
4639 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004640 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004641 return MakePair(Top::Throw(*reference_error), NULL);
4642 } else {
4643 // The property doesn't exist - return undefined
4644 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4645 }
4646}
4647
4648
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004649static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004650 return LoadContextSlotHelper(args, true);
4651}
4652
4653
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004654static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004655 return LoadContextSlotHelper(args, false);
4656}
4657
4658
4659static Object* Runtime_StoreContextSlot(Arguments args) {
4660 HandleScope scope;
4661 ASSERT(args.length() == 3);
4662
4663 Handle<Object> value(args[0]);
4664 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004665 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666
4667 int index;
4668 PropertyAttributes attributes;
4669 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004670 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004671 context->Lookup(name, flags, &index, &attributes);
4672
4673 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004674 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004675 // Ignore if read_only variable.
4676 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004677 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004678 }
4679 } else {
4680 ASSERT((attributes & READ_ONLY) == 0);
4681 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004682 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004683 USE(result);
4684 ASSERT(!result->IsFailure());
4685 }
4686 return *value;
4687 }
4688
4689 // Slow case: The property is not in a FixedArray context.
4690 // It is either in an JSObject extension context or it was not found.
4691 Handle<JSObject> context_ext;
4692
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004693 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004694 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004695 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004696 } else {
4697 // The property was not found. It needs to be stored in the global context.
4698 ASSERT(attributes == ABSENT);
4699 attributes = NONE;
4700 context_ext = Handle<JSObject>(Top::context()->global());
4701 }
4702
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004703 // Set the property, but ignore if read_only variable on the context
4704 // extension object itself.
4705 if ((attributes & READ_ONLY) == 0 ||
4706 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004707 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4708 if (set.is_null()) {
4709 // Failure::Exception is converted to a null handle in the
4710 // handle-based methods such as SetProperty. We therefore need
4711 // to convert null handles back to exceptions.
4712 ASSERT(Top::has_pending_exception());
4713 return Failure::Exception();
4714 }
4715 }
4716 return *value;
4717}
4718
4719
4720static Object* Runtime_Throw(Arguments args) {
4721 HandleScope scope;
4722 ASSERT(args.length() == 1);
4723
4724 return Top::Throw(args[0]);
4725}
4726
4727
4728static Object* Runtime_ReThrow(Arguments args) {
4729 HandleScope scope;
4730 ASSERT(args.length() == 1);
4731
4732 return Top::ReThrow(args[0]);
4733}
4734
4735
4736static Object* Runtime_ThrowReferenceError(Arguments args) {
4737 HandleScope scope;
4738 ASSERT(args.length() == 1);
4739
4740 Handle<Object> name(args[0]);
4741 Handle<Object> reference_error =
4742 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4743 return Top::Throw(*reference_error);
4744}
4745
4746
4747static Object* Runtime_StackOverflow(Arguments args) {
4748 NoHandleAllocation na;
4749 return Top::StackOverflow();
4750}
4751
4752
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004753static Object* Runtime_StackGuard(Arguments args) {
4754 ASSERT(args.length() == 1);
4755
4756 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004757 if (StackGuard::IsStackOverflow()) {
4758 return Runtime_StackOverflow(args);
4759 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004760
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004761 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004762}
4763
4764
4765// NOTE: These PrintXXX functions are defined for all builds (not just
4766// DEBUG builds) because we may want to be able to trace function
4767// calls in all modes.
4768static void PrintString(String* str) {
4769 // not uncommon to have empty strings
4770 if (str->length() > 0) {
4771 SmartPointer<char> s =
4772 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4773 PrintF("%s", *s);
4774 }
4775}
4776
4777
4778static void PrintObject(Object* obj) {
4779 if (obj->IsSmi()) {
4780 PrintF("%d", Smi::cast(obj)->value());
4781 } else if (obj->IsString() || obj->IsSymbol()) {
4782 PrintString(String::cast(obj));
4783 } else if (obj->IsNumber()) {
4784 PrintF("%g", obj->Number());
4785 } else if (obj->IsFailure()) {
4786 PrintF("<failure>");
4787 } else if (obj->IsUndefined()) {
4788 PrintF("<undefined>");
4789 } else if (obj->IsNull()) {
4790 PrintF("<null>");
4791 } else if (obj->IsTrue()) {
4792 PrintF("<true>");
4793 } else if (obj->IsFalse()) {
4794 PrintF("<false>");
4795 } else {
4796 PrintF("%p", obj);
4797 }
4798}
4799
4800
4801static int StackSize() {
4802 int n = 0;
4803 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4804 return n;
4805}
4806
4807
4808static void PrintTransition(Object* result) {
4809 // indentation
4810 { const int nmax = 80;
4811 int n = StackSize();
4812 if (n <= nmax)
4813 PrintF("%4d:%*s", n, n, "");
4814 else
4815 PrintF("%4d:%*s", n, nmax, "...");
4816 }
4817
4818 if (result == NULL) {
4819 // constructor calls
4820 JavaScriptFrameIterator it;
4821 JavaScriptFrame* frame = it.frame();
4822 if (frame->IsConstructor()) PrintF("new ");
4823 // function name
4824 Object* fun = frame->function();
4825 if (fun->IsJSFunction()) {
4826 PrintObject(JSFunction::cast(fun)->shared()->name());
4827 } else {
4828 PrintObject(fun);
4829 }
4830 // function arguments
4831 // (we are intentionally only printing the actually
4832 // supplied parameters, not all parameters required)
4833 PrintF("(this=");
4834 PrintObject(frame->receiver());
4835 const int length = frame->GetProvidedParametersCount();
4836 for (int i = 0; i < length; i++) {
4837 PrintF(", ");
4838 PrintObject(frame->GetParameter(i));
4839 }
4840 PrintF(") {\n");
4841
4842 } else {
4843 // function result
4844 PrintF("} -> ");
4845 PrintObject(result);
4846 PrintF("\n");
4847 }
4848}
4849
4850
4851static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004852 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853 NoHandleAllocation ha;
4854 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004855 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004856}
4857
4858
4859static Object* Runtime_TraceExit(Arguments args) {
4860 NoHandleAllocation ha;
4861 PrintTransition(args[0]);
4862 return args[0]; // return TOS
4863}
4864
4865
4866static Object* Runtime_DebugPrint(Arguments args) {
4867 NoHandleAllocation ha;
4868 ASSERT(args.length() == 1);
4869
4870#ifdef DEBUG
4871 if (args[0]->IsString()) {
4872 // If we have a string, assume it's a code "marker"
4873 // and print some interesting cpu debugging info.
4874 JavaScriptFrameIterator it;
4875 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004876 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4877 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878 } else {
4879 PrintF("DebugPrint: ");
4880 }
4881 args[0]->Print();
4882#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004883 // ShortPrint is available in release mode. Print is not.
4884 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004885#endif
4886 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004887 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004888
4889 return args[0]; // return TOS
4890}
4891
4892
4893static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004894 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895 NoHandleAllocation ha;
4896 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004897 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004898}
4899
4900
mads.s.ager31e71382008-08-13 09:32:07 +00004901static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004902 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004903 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004904
4905 // According to ECMA-262, section 15.9.1, page 117, the precision of
4906 // the number in a Date object representing a particular instant in
4907 // time is milliseconds. Therefore, we floor the result of getting
4908 // the OS time.
4909 double millis = floor(OS::TimeCurrentMillis());
4910 return Heap::NumberFromDouble(millis);
4911}
4912
4913
4914static Object* Runtime_DateParseString(Arguments args) {
4915 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004916 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004917
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004918 CONVERT_ARG_CHECKED(String, str, 0);
4919 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004920
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004921 CONVERT_ARG_CHECKED(JSArray, output, 1);
4922 RUNTIME_ASSERT(output->HasFastElements());
4923
4924 AssertNoAllocation no_allocation;
4925
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004926 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004927 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4928 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004929 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004930 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004931 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004932 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004933 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4934 }
4935
4936 if (result) {
4937 return *output;
4938 } else {
4939 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004940 }
4941}
4942
4943
4944static Object* Runtime_DateLocalTimezone(Arguments args) {
4945 NoHandleAllocation ha;
4946 ASSERT(args.length() == 1);
4947
4948 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00004949 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004950 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4951}
4952
4953
4954static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4955 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004956 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004957
4958 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4959}
4960
4961
4962static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4963 NoHandleAllocation ha;
4964 ASSERT(args.length() == 1);
4965
4966 CONVERT_DOUBLE_CHECKED(x, args[0]);
4967 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4968}
4969
4970
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004971static Object* Runtime_NumberIsFinite(Arguments args) {
4972 NoHandleAllocation ha;
4973 ASSERT(args.length() == 1);
4974
4975 CONVERT_DOUBLE_CHECKED(value, args[0]);
4976 Object* result;
4977 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4978 result = Heap::false_value();
4979 } else {
4980 result = Heap::true_value();
4981 }
4982 return result;
4983}
4984
4985
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004986static Object* Runtime_GlobalReceiver(Arguments args) {
4987 ASSERT(args.length() == 1);
4988 Object* global = args[0];
4989 if (!global->IsJSGlobalObject()) return Heap::null_value();
4990 return JSGlobalObject::cast(global)->global_receiver();
4991}
4992
4993
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004994static Object* Runtime_CompileString(Arguments args) {
4995 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004996 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004997 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004998 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004999
ager@chromium.org381abbb2009-02-25 13:23:22 +00005000 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005001 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005002 Compiler::ValidationState validate = (is_json->IsTrue())
5003 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005004 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5005 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005006 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005007 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005008 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005009 Handle<JSFunction> fun =
5010 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5011 return *fun;
5012}
5013
5014
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005015static Handle<JSFunction> GetBuiltinFunction(String* name) {
5016 LookupResult result;
5017 Top::global_context()->builtins()->LocalLookup(name, &result);
5018 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5019}
5020
5021
5022static Object* CompileDirectEval(Handle<String> source) {
5023 // Compute the eval context.
5024 HandleScope scope;
5025 StackFrameLocator locator;
5026 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5027 Handle<Context> context(Context::cast(frame->context()));
5028 bool is_global = context->IsGlobalContext();
5029
ager@chromium.org381abbb2009-02-25 13:23:22 +00005030 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005031 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5032 source,
5033 context,
5034 is_global,
5035 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005036 if (boilerplate.is_null()) return Failure::Exception();
5037 Handle<JSFunction> fun =
5038 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5039 return *fun;
5040}
5041
5042
5043static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5044 ASSERT(args.length() == 2);
5045
5046 HandleScope scope;
5047
5048 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5049
5050 Handle<Object> receiver;
5051
5052 // Find where the 'eval' symbol is bound. It is unaliased only if
5053 // it is bound in the global context.
5054 StackFrameLocator locator;
5055 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5056 Handle<Context> context(Context::cast(frame->context()));
5057 int index;
5058 PropertyAttributes attributes;
5059 while (!context.is_null()) {
5060 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5061 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005062 // Stop search when eval is found or when the global context is
5063 // reached.
5064 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005065 if (context->is_function_context()) {
5066 context = Handle<Context>(Context::cast(context->closure()->context()));
5067 } else {
5068 context = Handle<Context>(context->previous());
5069 }
5070 }
5071
iposva@chromium.org245aa852009-02-10 00:49:54 +00005072 // If eval could not be resolved, it has been deleted and we need to
5073 // throw a reference error.
5074 if (attributes == ABSENT) {
5075 Handle<Object> name = Factory::eval_symbol();
5076 Handle<Object> reference_error =
5077 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5078 return Top::Throw(*reference_error);
5079 }
5080
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005081 if (context->IsGlobalContext()) {
5082 // 'eval' is bound in the global context, but it may have been overwritten.
5083 // Compare it to the builtin 'GlobalEval' function to make sure.
5084 Handle<JSFunction> global_eval =
5085 GetBuiltinFunction(Heap::global_eval_symbol());
5086 if (global_eval.is_identical_to(callee)) {
5087 // A direct eval call.
5088 if (args[1]->IsString()) {
5089 CONVERT_ARG_CHECKED(String, source, 1);
5090 // A normal eval call on a string. Compile it and return the
5091 // compiled function bound in the local context.
5092 Object* compiled_source = CompileDirectEval(source);
5093 if (compiled_source->IsFailure()) return compiled_source;
5094 receiver = Handle<Object>(frame->receiver());
5095 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5096 } else {
5097 // An eval call that is not called on a string. Global eval
5098 // deals better with this.
5099 receiver = Handle<Object>(Top::global_context()->global());
5100 }
5101 } else {
5102 // 'eval' is overwritten. Just call the function with the given arguments.
5103 receiver = Handle<Object>(Top::global_context()->global());
5104 }
5105 } else {
5106 // 'eval' is not bound in the global context. Just call the function
5107 // with the given arguments. This is not necessarily the global eval.
5108 if (receiver->IsContext()) {
5109 context = Handle<Context>::cast(receiver);
5110 receiver = Handle<Object>(context->get(index));
5111 }
5112 }
5113
5114 Handle<FixedArray> call = Factory::NewFixedArray(2);
5115 call->set(0, *callee);
5116 call->set(1, *receiver);
5117 return *call;
5118}
5119
5120
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005121static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5122 // This utility adjusts the property attributes for newly created Function
5123 // object ("new Function(...)") by changing the map.
5124 // All it does is changing the prototype property to enumerable
5125 // as specified in ECMA262, 15.3.5.2.
5126 HandleScope scope;
5127 ASSERT(args.length() == 1);
5128 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5129 ASSERT(func->map()->instance_type() ==
5130 Top::function_instance_map()->instance_type());
5131 ASSERT(func->map()->instance_size() ==
5132 Top::function_instance_map()->instance_size());
5133 func->set_map(*Top::function_instance_map());
5134 return *func;
5135}
5136
5137
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005138// Push an array unto an array of arrays if it is not already in the
5139// array. Returns true if the element was pushed on the stack and
5140// false otherwise.
5141static Object* Runtime_PushIfAbsent(Arguments args) {
5142 ASSERT(args.length() == 2);
5143 CONVERT_CHECKED(JSArray, array, args[0]);
5144 CONVERT_CHECKED(JSArray, element, args[1]);
5145 RUNTIME_ASSERT(array->HasFastElements());
5146 int length = Smi::cast(array->length())->value();
5147 FixedArray* elements = FixedArray::cast(array->elements());
5148 for (int i = 0; i < length; i++) {
5149 if (elements->get(i) == element) return Heap::false_value();
5150 }
5151 Object* obj = array->SetFastElement(length, element);
5152 if (obj->IsFailure()) return obj;
5153 return Heap::true_value();
5154}
5155
5156
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005157/**
5158 * A simple visitor visits every element of Array's.
5159 * The backend storage can be a fixed array for fast elements case,
5160 * or a dictionary for sparse array. Since Dictionary is a subtype
5161 * of FixedArray, the class can be used by both fast and slow cases.
5162 * The second parameter of the constructor, fast_elements, specifies
5163 * whether the storage is a FixedArray or Dictionary.
5164 *
5165 * An index limit is used to deal with the situation that a result array
5166 * length overflows 32-bit non-negative integer.
5167 */
5168class ArrayConcatVisitor {
5169 public:
5170 ArrayConcatVisitor(Handle<FixedArray> storage,
5171 uint32_t index_limit,
5172 bool fast_elements) :
5173 storage_(storage), index_limit_(index_limit),
5174 fast_elements_(fast_elements), index_offset_(0) { }
5175
5176 void visit(uint32_t i, Handle<Object> elm) {
5177 uint32_t index = i + index_offset_;
5178 if (index >= index_limit_) return;
5179
5180 if (fast_elements_) {
5181 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5182 storage_->set(index, *elm);
5183
5184 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005185 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5186 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005187 Factory::DictionaryAtNumberPut(dict, index, elm);
5188 if (!result.is_identical_to(dict))
5189 storage_ = result;
5190 }
5191 }
5192
5193 void increase_index_offset(uint32_t delta) {
5194 index_offset_ += delta;
5195 }
5196
5197 private:
5198 Handle<FixedArray> storage_;
5199 uint32_t index_limit_;
5200 bool fast_elements_;
5201 uint32_t index_offset_;
5202};
5203
5204
5205/**
5206 * A helper function that visits elements of a JSObject. Only elements
5207 * whose index between 0 and range (exclusive) are visited.
5208 *
5209 * If the third parameter, visitor, is not NULL, the visitor is called
5210 * with parameters, 'visitor_index_offset + element index' and the element.
5211 *
5212 * It returns the number of visisted elements.
5213 */
5214static uint32_t IterateElements(Handle<JSObject> receiver,
5215 uint32_t range,
5216 ArrayConcatVisitor* visitor) {
5217 uint32_t num_of_elements = 0;
5218
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005219 switch (receiver->GetElementsKind()) {
5220 case JSObject::FAST_ELEMENTS: {
5221 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5222 uint32_t len = elements->length();
5223 if (range < len) {
5224 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005225 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005226
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005227 for (uint32_t j = 0; j < len; j++) {
5228 Handle<Object> e(elements->get(j));
5229 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005230 num_of_elements++;
5231 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005232 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005233 }
5234 }
5235 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005236 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005237 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005238 case JSObject::PIXEL_ELEMENTS: {
5239 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5240 uint32_t len = pixels->length();
5241 if (range < len) {
5242 len = range;
5243 }
5244
5245 for (uint32_t j = 0; j < len; j++) {
5246 num_of_elements++;
5247 if (visitor != NULL) {
5248 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5249 visitor->visit(j, e);
5250 }
5251 }
5252 break;
5253 }
5254 case JSObject::DICTIONARY_ELEMENTS: {
5255 Handle<NumberDictionary> dict(receiver->element_dictionary());
5256 uint32_t capacity = dict->Capacity();
5257 for (uint32_t j = 0; j < capacity; j++) {
5258 Handle<Object> k(dict->KeyAt(j));
5259 if (dict->IsKey(*k)) {
5260 ASSERT(k->IsNumber());
5261 uint32_t index = static_cast<uint32_t>(k->Number());
5262 if (index < range) {
5263 num_of_elements++;
5264 if (visitor) {
5265 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5266 }
5267 }
5268 }
5269 }
5270 break;
5271 }
5272 default:
5273 UNREACHABLE();
5274 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005275 }
5276
5277 return num_of_elements;
5278}
5279
5280
5281/**
5282 * A helper function that visits elements of an Array object, and elements
5283 * on its prototypes.
5284 *
5285 * Elements on prototypes are visited first, and only elements whose indices
5286 * less than Array length are visited.
5287 *
5288 * If a ArrayConcatVisitor object is given, the visitor is called with
5289 * parameters, element's index + visitor_index_offset and the element.
5290 */
5291static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5292 ArrayConcatVisitor* visitor) {
5293 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5294 Handle<Object> obj = array;
5295
5296 static const int kEstimatedPrototypes = 3;
5297 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5298
5299 // Visit prototype first. If an element on the prototype is shadowed by
5300 // the inheritor using the same index, the ArrayConcatVisitor visits
5301 // the prototype element before the shadowing element.
5302 // The visitor can simply overwrite the old value by new value using
5303 // the same index. This follows Array::concat semantics.
5304 while (!obj->IsNull()) {
5305 objects.Add(Handle<JSObject>::cast(obj));
5306 obj = Handle<Object>(obj->GetPrototype());
5307 }
5308
5309 uint32_t nof_elements = 0;
5310 for (int i = objects.length() - 1; i >= 0; i--) {
5311 Handle<JSObject> obj = objects[i];
5312 nof_elements +=
5313 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5314 }
5315
5316 return nof_elements;
5317}
5318
5319
5320/**
5321 * A helper function of Runtime_ArrayConcat.
5322 *
5323 * The first argument is an Array of arrays and objects. It is the
5324 * same as the arguments array of Array::concat JS function.
5325 *
5326 * If an argument is an Array object, the function visits array
5327 * elements. If an argument is not an Array object, the function
5328 * visits the object as if it is an one-element array.
5329 *
5330 * If the result array index overflows 32-bit integer, the rounded
5331 * non-negative number is used as new length. For example, if one
5332 * array length is 2^32 - 1, second array length is 1, the
5333 * concatenated array length is 0.
5334 */
5335static uint32_t IterateArguments(Handle<JSArray> arguments,
5336 ArrayConcatVisitor* visitor) {
5337 uint32_t visited_elements = 0;
5338 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5339
5340 for (uint32_t i = 0; i < num_of_args; i++) {
5341 Handle<Object> obj(arguments->GetElement(i));
5342 if (obj->IsJSArray()) {
5343 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5344 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5345 uint32_t nof_elements =
5346 IterateArrayAndPrototypeElements(array, visitor);
5347 // Total elements of array and its prototype chain can be more than
5348 // the array length, but ArrayConcat can only concatenate at most
5349 // the array length number of elements.
5350 visited_elements += (nof_elements > len) ? len : nof_elements;
5351 if (visitor) visitor->increase_index_offset(len);
5352
5353 } else {
5354 if (visitor) {
5355 visitor->visit(0, obj);
5356 visitor->increase_index_offset(1);
5357 }
5358 visited_elements++;
5359 }
5360 }
5361 return visited_elements;
5362}
5363
5364
5365/**
5366 * Array::concat implementation.
5367 * See ECMAScript 262, 15.4.4.4.
5368 */
5369static Object* Runtime_ArrayConcat(Arguments args) {
5370 ASSERT(args.length() == 1);
5371 HandleScope handle_scope;
5372
5373 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5374 Handle<JSArray> arguments(arg_arrays);
5375
5376 // Pass 1: estimate the number of elements of the result
5377 // (it could be more than real numbers if prototype has elements).
5378 uint32_t result_length = 0;
5379 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5380
5381 { AssertNoAllocation nogc;
5382 for (uint32_t i = 0; i < num_of_args; i++) {
5383 Object* obj = arguments->GetElement(i);
5384 if (obj->IsJSArray()) {
5385 result_length +=
5386 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5387 } else {
5388 result_length++;
5389 }
5390 }
5391 }
5392
5393 // Allocate an empty array, will set length and content later.
5394 Handle<JSArray> result = Factory::NewJSArray(0);
5395
5396 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5397 // If estimated number of elements is more than half of length, a
5398 // fixed array (fast case) is more time and space-efficient than a
5399 // dictionary.
5400 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5401
5402 Handle<FixedArray> storage;
5403 if (fast_case) {
5404 // The backing storage array must have non-existing elements to
5405 // preserve holes across concat operations.
5406 storage = Factory::NewFixedArrayWithHoles(result_length);
5407
5408 } else {
5409 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5410 uint32_t at_least_space_for = estimate_nof_elements +
5411 (estimate_nof_elements >> 2);
5412 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005413 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005414 }
5415
5416 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5417
5418 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5419
5420 IterateArguments(arguments, &visitor);
5421
5422 result->set_length(*len);
5423 result->set_elements(*storage);
5424
5425 return *result;
5426}
5427
5428
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005429// This will not allocate (flatten the string), but it may run
5430// very slowly for very deeply nested ConsStrings. For debugging use only.
5431static Object* Runtime_GlobalPrint(Arguments args) {
5432 NoHandleAllocation ha;
5433 ASSERT(args.length() == 1);
5434
5435 CONVERT_CHECKED(String, string, args[0]);
5436 StringInputBuffer buffer(string);
5437 while (buffer.has_more()) {
5438 uint16_t character = buffer.GetNext();
5439 PrintF("%c", character);
5440 }
5441 return string;
5442}
5443
ager@chromium.org5ec48922009-05-05 07:25:34 +00005444// Moves all own elements of an object, that are below a limit, to positions
5445// starting at zero. All undefined values are placed after non-undefined values,
5446// and are followed by non-existing element. Does not change the length
5447// property.
5448// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005449static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005450 ASSERT(args.length() == 2);
5451 CONVERT_CHECKED(JSObject, object, args[0]);
5452 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5453 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005454}
5455
5456
5457// Move contents of argument 0 (an array) to argument 1 (an array)
5458static Object* Runtime_MoveArrayContents(Arguments args) {
5459 ASSERT(args.length() == 2);
5460 CONVERT_CHECKED(JSArray, from, args[0]);
5461 CONVERT_CHECKED(JSArray, to, args[1]);
5462 to->SetContent(FixedArray::cast(from->elements()));
5463 to->set_length(from->length());
5464 from->SetContent(Heap::empty_fixed_array());
5465 from->set_length(0);
5466 return to;
5467}
5468
5469
5470// How many elements does this array have?
5471static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5472 ASSERT(args.length() == 1);
5473 CONVERT_CHECKED(JSArray, array, args[0]);
5474 HeapObject* elements = array->elements();
5475 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005476 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005477 } else {
5478 return array->length();
5479 }
5480}
5481
5482
5483// Returns an array that tells you where in the [0, length) interval an array
5484// might have elements. Can either return keys or intervals. Keys can have
5485// gaps in (undefined). Intervals can also span over some undefined keys.
5486static Object* Runtime_GetArrayKeys(Arguments args) {
5487 ASSERT(args.length() == 2);
5488 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005489 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005490 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005491 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005492 // Create an array and get all the keys into it, then remove all the
5493 // keys that are not integers in the range 0 to length-1.
5494 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5495 int keys_length = keys->length();
5496 for (int i = 0; i < keys_length; i++) {
5497 Object* key = keys->get(i);
5498 uint32_t index;
5499 if (!Array::IndexFromObject(key, &index) || index >= length) {
5500 // Zap invalid keys.
5501 keys->set_undefined(i);
5502 }
5503 }
5504 return *Factory::NewJSArrayWithElements(keys);
5505 } else {
5506 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5507 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005508 single_interval->set(0,
5509 Smi::FromInt(-1),
5510 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005511 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5512 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005513 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005514 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005515 single_interval->set(1, *length_object);
5516 return *Factory::NewJSArrayWithElements(single_interval);
5517 }
5518}
5519
5520
5521// DefineAccessor takes an optional final argument which is the
5522// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5523// to the way accessors are implemented, it is set for both the getter
5524// and setter on the first call to DefineAccessor and ignored on
5525// subsequent calls.
5526static Object* Runtime_DefineAccessor(Arguments args) {
5527 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5528 // Compute attributes.
5529 PropertyAttributes attributes = NONE;
5530 if (args.length() == 5) {
5531 CONVERT_CHECKED(Smi, attrs, args[4]);
5532 int value = attrs->value();
5533 // Only attribute bits should be set.
5534 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5535 attributes = static_cast<PropertyAttributes>(value);
5536 }
5537
5538 CONVERT_CHECKED(JSObject, obj, args[0]);
5539 CONVERT_CHECKED(String, name, args[1]);
5540 CONVERT_CHECKED(Smi, flag, args[2]);
5541 CONVERT_CHECKED(JSFunction, fun, args[3]);
5542 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5543}
5544
5545
5546static Object* Runtime_LookupAccessor(Arguments args) {
5547 ASSERT(args.length() == 3);
5548 CONVERT_CHECKED(JSObject, obj, args[0]);
5549 CONVERT_CHECKED(String, name, args[1]);
5550 CONVERT_CHECKED(Smi, flag, args[2]);
5551 return obj->LookupAccessor(name, flag->value() == 0);
5552}
5553
5554
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005555#ifdef ENABLE_DEBUGGER_SUPPORT
5556static Object* Runtime_DebugBreak(Arguments args) {
5557 ASSERT(args.length() == 0);
5558 return Execution::DebugBreakHelper();
5559}
5560
5561
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005562// Helper functions for wrapping and unwrapping stack frame ids.
5563static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005564 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005565 return Smi::FromInt(id >> 2);
5566}
5567
5568
5569static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5570 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5571}
5572
5573
5574// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005575// args[0]: debug event listener function to set or null or undefined for
5576// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005577// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005578static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005579 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005580 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5581 args[0]->IsUndefined() ||
5582 args[0]->IsNull());
5583 Handle<Object> callback = args.at<Object>(0);
5584 Handle<Object> data = args.at<Object>(1);
5585 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586
5587 return Heap::undefined_value();
5588}
5589
5590
5591static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005592 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005593 StackGuard::DebugBreak();
5594 return Heap::undefined_value();
5595}
5596
5597
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005598// Find the length of the prototype chain that is to to handled as one. If a
5599// prototype object is hidden it is to be viewed as part of the the object it
5600// is prototype for.
5601static int LocalPrototypeChainLength(JSObject* obj) {
5602 int count = 1;
5603 Object* proto = obj->GetPrototype();
5604 while (proto->IsJSObject() &&
5605 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5606 count++;
5607 proto = JSObject::cast(proto)->GetPrototype();
5608 }
5609 return count;
5610}
5611
5612
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005613static Object* DebugLookupResultValue(Object* receiver, String* name,
5614 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005615 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005616 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005617 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005618 case NORMAL:
5619 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005620 if (value->IsTheHole()) {
5621 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005622 }
5623 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005624 case FIELD:
5625 value =
5626 JSObject::cast(
5627 result->holder())->FastPropertyAt(result->GetFieldIndex());
5628 if (value->IsTheHole()) {
5629 return Heap::undefined_value();
5630 }
5631 return value;
5632 case CONSTANT_FUNCTION:
5633 return result->GetConstantFunction();
5634 case CALLBACKS: {
5635 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005636 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005637 value = receiver->GetPropertyWithCallback(
5638 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005639 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005640 value = Top::pending_exception();
5641 Top::clear_pending_exception();
5642 if (caught_exception != NULL) {
5643 *caught_exception = true;
5644 }
5645 }
5646 return value;
5647 } else {
5648 return Heap::undefined_value();
5649 }
5650 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005651 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005652 case MAP_TRANSITION:
5653 case CONSTANT_TRANSITION:
5654 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005655 return Heap::undefined_value();
5656 default:
5657 UNREACHABLE();
5658 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005659 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005660 return Heap::undefined_value();
5661}
5662
5663
ager@chromium.org32912102009-01-16 10:38:43 +00005664// Get debugger related details for an object property.
5665// args[0]: object holding property
5666// args[1]: name of the property
5667//
5668// The array returned contains the following information:
5669// 0: Property value
5670// 1: Property details
5671// 2: Property value is exception
5672// 3: Getter function if defined
5673// 4: Setter function if defined
5674// Items 2-4 are only filled if the property has either a getter or a setter
5675// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005676static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005677 HandleScope scope;
5678
5679 ASSERT(args.length() == 2);
5680
5681 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5682 CONVERT_ARG_CHECKED(String, name, 1);
5683
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005684 // Make sure to set the current context to the context before the debugger was
5685 // entered (if the debugger is entered). The reason for switching context here
5686 // is that for some property lookups (accessors and interceptors) callbacks
5687 // into the embedding application can occour, and the embedding application
5688 // could have the assumption that its own global context is the current
5689 // context and not some internal debugger context.
5690 SaveContext save;
5691 if (Debug::InDebugger()) {
5692 Top::set_context(*Debug::debugger_entry()->GetContext());
5693 }
5694
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005695 // Skip the global proxy as it has no properties and always delegates to the
5696 // real global object.
5697 if (obj->IsJSGlobalProxy()) {
5698 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5699 }
5700
5701
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005702 // Check if the name is trivially convertible to an index and get the element
5703 // if so.
5704 uint32_t index;
5705 if (name->AsArrayIndex(&index)) {
5706 Handle<FixedArray> details = Factory::NewFixedArray(2);
5707 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5708 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5709 return *Factory::NewJSArrayWithElements(details);
5710 }
5711
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005712 // Find the number of objects making up this.
5713 int length = LocalPrototypeChainLength(*obj);
5714
5715 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005716 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005717 Handle<JSObject> jsproto = obj;
5718 for (int i = 0; i < length; i++) {
5719 jsproto->LocalLookup(*name, &result);
5720 if (result.IsProperty()) {
5721 break;
5722 }
5723 if (i < length - 1) {
5724 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5725 }
5726 }
5727
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005728 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005729 // LookupResult is not GC safe as all its members are raw object pointers.
5730 // When calling DebugLookupResultValue GC can happen as this might invoke
5731 // callbacks. After the call to DebugLookupResultValue the callback object
5732 // in the LookupResult might still be needed. Put it into a handle for later
5733 // use.
5734 PropertyType result_type = result.type();
5735 Handle<Object> result_callback_obj;
5736 if (result_type == CALLBACKS) {
5737 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5738 }
5739
5740 // Find the actual value. Don't use result after this call as it's content
5741 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005742 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005743 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005744 &caught_exception);
5745 if (value->IsFailure()) return value;
5746 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005747
ager@chromium.org32912102009-01-16 10:38:43 +00005748 // If the callback object is a fixed array then it contains JavaScript
5749 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005750 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5751 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005752 Handle<FixedArray> details =
5753 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005754 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005755 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005756 if (hasJavaScriptAccessors) {
5757 details->set(2,
5758 caught_exception ? Heap::true_value() : Heap::false_value());
5759 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5760 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5761 }
5762
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005763 return *Factory::NewJSArrayWithElements(details);
5764 }
5765 return Heap::undefined_value();
5766}
5767
5768
5769static Object* Runtime_DebugGetProperty(Arguments args) {
5770 HandleScope scope;
5771
5772 ASSERT(args.length() == 2);
5773
5774 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5775 CONVERT_ARG_CHECKED(String, name, 1);
5776
5777 LookupResult result;
5778 obj->Lookup(*name, &result);
5779 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005780 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005781 }
5782 return Heap::undefined_value();
5783}
5784
5785
5786// Return the names of the local named properties.
5787// args[0]: object
5788static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5789 HandleScope scope;
5790 ASSERT(args.length() == 1);
5791 if (!args[0]->IsJSObject()) {
5792 return Heap::undefined_value();
5793 }
5794 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5795
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005796 // Skip the global proxy as it has no properties and always delegates to the
5797 // real global object.
5798 if (obj->IsJSGlobalProxy()) {
5799 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5800 }
5801
5802 // Find the number of objects making up this.
5803 int length = LocalPrototypeChainLength(*obj);
5804
5805 // Find the number of local properties for each of the objects.
5806 int* local_property_count = NewArray<int>(length);
5807 int total_property_count = 0;
5808 Handle<JSObject> jsproto = obj;
5809 for (int i = 0; i < length; i++) {
5810 int n;
5811 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5812 local_property_count[i] = n;
5813 total_property_count += n;
5814 if (i < length - 1) {
5815 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5816 }
5817 }
5818
5819 // Allocate an array with storage for all the property names.
5820 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5821
5822 // Get the property names.
5823 jsproto = obj;
5824 for (int i = 0; i < length; i++) {
5825 jsproto->GetLocalPropertyNames(*names,
5826 i == 0 ? 0 : local_property_count[i - 1]);
5827 if (i < length - 1) {
5828 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5829 }
5830 }
5831
5832 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005833 return *Factory::NewJSArrayWithElements(names);
5834}
5835
5836
5837// Return the names of the local indexed properties.
5838// args[0]: object
5839static Object* Runtime_DebugLocalElementNames(Arguments args) {
5840 HandleScope scope;
5841 ASSERT(args.length() == 1);
5842 if (!args[0]->IsJSObject()) {
5843 return Heap::undefined_value();
5844 }
5845 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5846
5847 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5848 Handle<FixedArray> names = Factory::NewFixedArray(n);
5849 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5850 return *Factory::NewJSArrayWithElements(names);
5851}
5852
5853
5854// Return the property type calculated from the property details.
5855// args[0]: smi with property details.
5856static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5857 ASSERT(args.length() == 1);
5858 CONVERT_CHECKED(Smi, details, args[0]);
5859 PropertyType type = PropertyDetails(details).type();
5860 return Smi::FromInt(static_cast<int>(type));
5861}
5862
5863
5864// Return the property attribute calculated from the property details.
5865// args[0]: smi with property details.
5866static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5867 ASSERT(args.length() == 1);
5868 CONVERT_CHECKED(Smi, details, args[0]);
5869 PropertyAttributes attributes = PropertyDetails(details).attributes();
5870 return Smi::FromInt(static_cast<int>(attributes));
5871}
5872
5873
5874// Return the property insertion index calculated from the property details.
5875// args[0]: smi with property details.
5876static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5877 ASSERT(args.length() == 1);
5878 CONVERT_CHECKED(Smi, details, args[0]);
5879 int index = PropertyDetails(details).index();
5880 return Smi::FromInt(index);
5881}
5882
5883
5884// Return information on whether an object has a named or indexed interceptor.
5885// args[0]: object
5886static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5887 HandleScope scope;
5888 ASSERT(args.length() == 1);
5889 if (!args[0]->IsJSObject()) {
5890 return Smi::FromInt(0);
5891 }
5892 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5893
5894 int result = 0;
5895 if (obj->HasNamedInterceptor()) result |= 2;
5896 if (obj->HasIndexedInterceptor()) result |= 1;
5897
5898 return Smi::FromInt(result);
5899}
5900
5901
5902// Return property names from named interceptor.
5903// args[0]: object
5904static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5905 HandleScope scope;
5906 ASSERT(args.length() == 1);
5907 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005908
ager@chromium.org32912102009-01-16 10:38:43 +00005909 if (obj->HasNamedInterceptor()) {
5910 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5911 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5912 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005913 return Heap::undefined_value();
5914}
5915
5916
5917// Return element names from indexed interceptor.
5918// args[0]: object
5919static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5920 HandleScope scope;
5921 ASSERT(args.length() == 1);
5922 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005923
ager@chromium.org32912102009-01-16 10:38:43 +00005924 if (obj->HasIndexedInterceptor()) {
5925 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5926 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5927 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005928 return Heap::undefined_value();
5929}
5930
5931
5932// Return property value from named interceptor.
5933// args[0]: object
5934// args[1]: property name
5935static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5936 HandleScope scope;
5937 ASSERT(args.length() == 2);
5938 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5939 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5940 CONVERT_ARG_CHECKED(String, name, 1);
5941
5942 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005943 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005944}
5945
5946
5947// Return element value from indexed interceptor.
5948// args[0]: object
5949// args[1]: index
5950static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5951 HandleScope scope;
5952 ASSERT(args.length() == 2);
5953 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5954 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5955 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5956
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005957 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005958}
5959
5960
5961static Object* Runtime_CheckExecutionState(Arguments args) {
5962 ASSERT(args.length() >= 1);
5963 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005964 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005965 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005966 return Top::Throw(Heap::illegal_execution_state_symbol());
5967 }
5968
5969 return Heap::true_value();
5970}
5971
5972
5973static Object* Runtime_GetFrameCount(Arguments args) {
5974 HandleScope scope;
5975 ASSERT(args.length() == 1);
5976
5977 // Check arguments.
5978 Object* result = Runtime_CheckExecutionState(args);
5979 if (result->IsFailure()) return result;
5980
5981 // Count all frames which are relevant to debugging stack trace.
5982 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005983 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005984 if (id == StackFrame::NO_ID) {
5985 // If there is no JavaScript stack frame count is 0.
5986 return Smi::FromInt(0);
5987 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005988 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5989 return Smi::FromInt(n);
5990}
5991
5992
5993static const int kFrameDetailsFrameIdIndex = 0;
5994static const int kFrameDetailsReceiverIndex = 1;
5995static const int kFrameDetailsFunctionIndex = 2;
5996static const int kFrameDetailsArgumentCountIndex = 3;
5997static const int kFrameDetailsLocalCountIndex = 4;
5998static const int kFrameDetailsSourcePositionIndex = 5;
5999static const int kFrameDetailsConstructCallIndex = 6;
6000static const int kFrameDetailsDebuggerFrameIndex = 7;
6001static const int kFrameDetailsFirstDynamicIndex = 8;
6002
6003// Return an array with frame details
6004// args[0]: number: break id
6005// args[1]: number: frame index
6006//
6007// The array returned contains the following information:
6008// 0: Frame id
6009// 1: Receiver
6010// 2: Function
6011// 3: Argument count
6012// 4: Local count
6013// 5: Source position
6014// 6: Constructor call
6015// 7: Debugger frame
6016// Arguments name, value
6017// Locals name, value
6018static Object* Runtime_GetFrameDetails(Arguments args) {
6019 HandleScope scope;
6020 ASSERT(args.length() == 2);
6021
6022 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006023 Object* check = Runtime_CheckExecutionState(args);
6024 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006025 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6026
6027 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006028 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006029 if (id == StackFrame::NO_ID) {
6030 // If there are no JavaScript stack frames return undefined.
6031 return Heap::undefined_value();
6032 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006033 int count = 0;
6034 JavaScriptFrameIterator it(id);
6035 for (; !it.done(); it.Advance()) {
6036 if (count == index) break;
6037 count++;
6038 }
6039 if (it.done()) return Heap::undefined_value();
6040
6041 // Traverse the saved contexts chain to find the active context for the
6042 // selected frame.
6043 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006044 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006045 save = save->prev();
6046 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006047 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006048
6049 // Get the frame id.
6050 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6051
6052 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006053 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006054
6055 // Check for constructor frame.
6056 bool constructor = it.frame()->IsConstructor();
6057
6058 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006059 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006060 ScopeInfo<> info(*code);
6061
6062 // Get the context.
6063 Handle<Context> context(Context::cast(it.frame()->context()));
6064
6065 // Get the locals names and values into a temporary array.
6066 //
6067 // TODO(1240907): Hide compiler-introduced stack variables
6068 // (e.g. .result)? For users of the debugger, they will probably be
6069 // confusing.
6070 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6071 for (int i = 0; i < info.NumberOfLocals(); i++) {
6072 // Name of the local.
6073 locals->set(i * 2, *info.LocalName(i));
6074
6075 // Fetch the value of the local - either from the stack or from a
6076 // heap-allocated context.
6077 if (i < info.number_of_stack_slots()) {
6078 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6079 } else {
6080 Handle<String> name = info.LocalName(i);
6081 // Traverse the context chain to the function context as all local
6082 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006083 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006084 context = Handle<Context>(context->previous());
6085 }
6086 ASSERT(context->is_function_context());
6087 locals->set(i * 2 + 1,
6088 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6089 NULL)));
6090 }
6091 }
6092
6093 // Now advance to the arguments adapter frame (if any). If contains all
6094 // the provided parameters and
6095
6096 // Now advance to the arguments adapter frame (if any). It contains all
6097 // the provided parameters whereas the function frame always have the number
6098 // of arguments matching the functions parameters. The rest of the
6099 // information (except for what is collected above) is the same.
6100 it.AdvanceToArgumentsFrame();
6101
6102 // Find the number of arguments to fill. At least fill the number of
6103 // parameters for the function and fill more if more parameters are provided.
6104 int argument_count = info.number_of_parameters();
6105 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6106 argument_count = it.frame()->GetProvidedParametersCount();
6107 }
6108
6109 // Calculate the size of the result.
6110 int details_size = kFrameDetailsFirstDynamicIndex +
6111 2 * (argument_count + info.NumberOfLocals());
6112 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6113
6114 // Add the frame id.
6115 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6116
6117 // Add the function (same as in function frame).
6118 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6119
6120 // Add the arguments count.
6121 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6122
6123 // Add the locals count
6124 details->set(kFrameDetailsLocalCountIndex,
6125 Smi::FromInt(info.NumberOfLocals()));
6126
6127 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006128 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006129 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6130 } else {
6131 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6132 }
6133
6134 // Add the constructor information.
6135 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6136
6137 // Add information on whether this frame is invoked in the debugger context.
6138 details->set(kFrameDetailsDebuggerFrameIndex,
6139 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6140
6141 // Fill the dynamic part.
6142 int details_index = kFrameDetailsFirstDynamicIndex;
6143
6144 // Add arguments name and value.
6145 for (int i = 0; i < argument_count; i++) {
6146 // Name of the argument.
6147 if (i < info.number_of_parameters()) {
6148 details->set(details_index++, *info.parameter_name(i));
6149 } else {
6150 details->set(details_index++, Heap::undefined_value());
6151 }
6152
6153 // Parameter value.
6154 if (i < it.frame()->GetProvidedParametersCount()) {
6155 details->set(details_index++, it.frame()->GetParameter(i));
6156 } else {
6157 details->set(details_index++, Heap::undefined_value());
6158 }
6159 }
6160
6161 // Add locals name and value from the temporary copy from the function frame.
6162 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6163 details->set(details_index++, locals->get(i));
6164 }
6165
6166 // Add the receiver (same as in function frame).
6167 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6168 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6169 Handle<Object> receiver(it.frame()->receiver());
6170 if (!receiver->IsJSObject()) {
6171 // If the receiver is NOT a JSObject we have hit an optimization
6172 // where a value object is not converted into a wrapped JS objects.
6173 // To hide this optimization from the debugger, we wrap the receiver
6174 // by creating correct wrapper object based on the calling frame's
6175 // global context.
6176 it.Advance();
6177 Handle<Context> calling_frames_global_context(
6178 Context::cast(Context::cast(it.frame()->context())->global_context()));
6179 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6180 }
6181 details->set(kFrameDetailsReceiverIndex, *receiver);
6182
6183 ASSERT_EQ(details_size, details_index);
6184 return *Factory::NewJSArrayWithElements(details);
6185}
6186
6187
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006188// Copy all the context locals into an object used to materialize a scope.
6189static void CopyContextLocalsToScopeObject(Handle<Code> code,
6190 ScopeInfo<>& scope_info,
6191 Handle<Context> context,
6192 Handle<JSObject> scope_object) {
6193 // Fill all context locals to the context extension.
6194 for (int i = Context::MIN_CONTEXT_SLOTS;
6195 i < scope_info.number_of_context_slots();
6196 i++) {
6197 int context_index =
6198 ScopeInfo<>::ContextSlotIndex(*code,
6199 *scope_info.context_slot_name(i),
6200 NULL);
6201
6202 // Don't include the arguments shadow (.arguments) context variable.
6203 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6204 SetProperty(scope_object,
6205 scope_info.context_slot_name(i),
6206 Handle<Object>(context->get(context_index)), NONE);
6207 }
6208 }
6209}
6210
6211
6212// Create a plain JSObject which materializes the local scope for the specified
6213// frame.
6214static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6215 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6216 Handle<Code> code(function->code());
6217 ScopeInfo<> scope_info(*code);
6218
6219 // Allocate and initialize a JSObject with all the arguments, stack locals
6220 // heap locals and extension properties of the debugged function.
6221 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6222
6223 // First fill all parameters.
6224 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6225 SetProperty(local_scope,
6226 scope_info.parameter_name(i),
6227 Handle<Object>(frame->GetParameter(i)), NONE);
6228 }
6229
6230 // Second fill all stack locals.
6231 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6232 SetProperty(local_scope,
6233 scope_info.stack_slot_name(i),
6234 Handle<Object>(frame->GetExpression(i)), NONE);
6235 }
6236
6237 // Third fill all context locals.
6238 Handle<Context> frame_context(Context::cast(frame->context()));
6239 Handle<Context> function_context(frame_context->fcontext());
6240 CopyContextLocalsToScopeObject(code, scope_info,
6241 function_context, local_scope);
6242
6243 // Finally copy any properties from the function context extension. This will
6244 // be variables introduced by eval.
6245 if (function_context->closure() == *function) {
6246 if (function_context->has_extension() &&
6247 !function_context->IsGlobalContext()) {
6248 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6249 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6250 for (int i = 0; i < keys->length(); i++) {
6251 // Names of variables introduced by eval are strings.
6252 ASSERT(keys->get(i)->IsString());
6253 Handle<String> key(String::cast(keys->get(i)));
6254 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6255 }
6256 }
6257 }
6258 return local_scope;
6259}
6260
6261
6262// Create a plain JSObject which materializes the closure content for the
6263// context.
6264static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6265 ASSERT(context->is_function_context());
6266
6267 Handle<Code> code(context->closure()->code());
6268 ScopeInfo<> scope_info(*code);
6269
6270 // Allocate and initialize a JSObject with all the content of theis function
6271 // closure.
6272 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6273
6274 // Check whether the arguments shadow object exists.
6275 int arguments_shadow_index =
6276 ScopeInfo<>::ContextSlotIndex(*code,
6277 Heap::arguments_shadow_symbol(),
6278 NULL);
6279 if (arguments_shadow_index >= 0) {
6280 // In this case all the arguments are available in the arguments shadow
6281 // object.
6282 Handle<JSObject> arguments_shadow(
6283 JSObject::cast(context->get(arguments_shadow_index)));
6284 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6285 SetProperty(closure_scope,
6286 scope_info.parameter_name(i),
6287 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6288 }
6289 }
6290
6291 // Fill all context locals to the context extension.
6292 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6293
6294 // Finally copy any properties from the function context extension. This will
6295 // be variables introduced by eval.
6296 if (context->has_extension()) {
6297 Handle<JSObject> ext(JSObject::cast(context->extension()));
6298 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6299 for (int i = 0; i < keys->length(); i++) {
6300 // Names of variables introduced by eval are strings.
6301 ASSERT(keys->get(i)->IsString());
6302 Handle<String> key(String::cast(keys->get(i)));
6303 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6304 }
6305 }
6306
6307 return closure_scope;
6308}
6309
6310
6311// Iterate over the actual scopes visible from a stack frame. All scopes are
6312// backed by an actual context except the local scope, which is inserted
6313// "artifically" in the context chain.
6314class ScopeIterator {
6315 public:
6316 enum ScopeType {
6317 ScopeTypeGlobal = 0,
6318 ScopeTypeLocal,
6319 ScopeTypeWith,
6320 ScopeTypeClosure
6321 };
6322
6323 explicit ScopeIterator(JavaScriptFrame* frame)
6324 : frame_(frame),
6325 function_(JSFunction::cast(frame->function())),
6326 context_(Context::cast(frame->context())),
6327 local_done_(false),
6328 at_local_(false) {
6329
6330 // Check whether the first scope is actually a local scope.
6331 if (context_->IsGlobalContext()) {
6332 // If there is a stack slot for .result then this local scope has been
6333 // created for evaluating top level code and it is not a real local scope.
6334 // Checking for the existence of .result seems fragile, but the scope info
6335 // saved with the code object does not otherwise have that information.
6336 Handle<Code> code(function_->code());
6337 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6338 at_local_ = index < 0;
6339 } else if (context_->is_function_context()) {
6340 at_local_ = true;
6341 }
6342 }
6343
6344 // More scopes?
6345 bool Done() { return context_.is_null(); }
6346
6347 // Move to the next scope.
6348 void Next() {
6349 // If at a local scope mark the local scope as passed.
6350 if (at_local_) {
6351 at_local_ = false;
6352 local_done_ = true;
6353
6354 // If the current context is not associated with the local scope the
6355 // current context is the next real scope, so don't move to the next
6356 // context in this case.
6357 if (context_->closure() != *function_) {
6358 return;
6359 }
6360 }
6361
6362 // The global scope is always the last in the chain.
6363 if (context_->IsGlobalContext()) {
6364 context_ = Handle<Context>();
6365 return;
6366 }
6367
6368 // Move to the next context.
6369 if (context_->is_function_context()) {
6370 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6371 } else {
6372 context_ = Handle<Context>(context_->previous());
6373 }
6374
6375 // If passing the local scope indicate that the current scope is now the
6376 // local scope.
6377 if (!local_done_ &&
6378 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6379 at_local_ = true;
6380 }
6381 }
6382
6383 // Return the type of the current scope.
6384 int Type() {
6385 if (at_local_) {
6386 return ScopeTypeLocal;
6387 }
6388 if (context_->IsGlobalContext()) {
6389 ASSERT(context_->global()->IsGlobalObject());
6390 return ScopeTypeGlobal;
6391 }
6392 if (context_->is_function_context()) {
6393 return ScopeTypeClosure;
6394 }
6395 ASSERT(context_->has_extension());
6396 ASSERT(!context_->extension()->IsJSContextExtensionObject());
6397 return ScopeTypeWith;
6398 }
6399
6400 // Return the JavaScript object with the content of the current scope.
6401 Handle<JSObject> ScopeObject() {
6402 switch (Type()) {
6403 case ScopeIterator::ScopeTypeGlobal:
6404 return Handle<JSObject>(CurrentContext()->global());
6405 break;
6406 case ScopeIterator::ScopeTypeLocal:
6407 // Materialize the content of the local scope into a JSObject.
6408 return MaterializeLocalScope(frame_);
6409 break;
6410 case ScopeIterator::ScopeTypeWith:
6411 // Return the with object.
6412 return Handle<JSObject>(CurrentContext()->extension());
6413 break;
6414 case ScopeIterator::ScopeTypeClosure:
6415 // Materialize the content of the closure scope into a JSObject.
6416 return MaterializeClosure(CurrentContext());
6417 break;
6418 }
6419 UNREACHABLE();
6420 return Handle<JSObject>();
6421 }
6422
6423 // Return the context for this scope. For the local context there might not
6424 // be an actual context.
6425 Handle<Context> CurrentContext() {
6426 if (at_local_ && context_->closure() != *function_) {
6427 return Handle<Context>();
6428 }
6429 return context_;
6430 }
6431
6432#ifdef DEBUG
6433 // Debug print of the content of the current scope.
6434 void DebugPrint() {
6435 switch (Type()) {
6436 case ScopeIterator::ScopeTypeGlobal:
6437 PrintF("Global:\n");
6438 CurrentContext()->Print();
6439 break;
6440
6441 case ScopeIterator::ScopeTypeLocal: {
6442 PrintF("Local:\n");
6443 Handle<Code> code(function_->code());
6444 ScopeInfo<> scope_info(*code);
6445 scope_info.Print();
6446 if (!CurrentContext().is_null()) {
6447 CurrentContext()->Print();
6448 if (CurrentContext()->has_extension()) {
6449 Handle<JSObject> extension =
6450 Handle<JSObject>(CurrentContext()->extension());
6451 if (extension->IsJSContextExtensionObject()) {
6452 extension->Print();
6453 }
6454 }
6455 }
6456 break;
6457 }
6458
6459 case ScopeIterator::ScopeTypeWith: {
6460 PrintF("With:\n");
6461 Handle<JSObject> extension =
6462 Handle<JSObject>(CurrentContext()->extension());
6463 extension->Print();
6464 break;
6465 }
6466
6467 case ScopeIterator::ScopeTypeClosure: {
6468 PrintF("Closure:\n");
6469 CurrentContext()->Print();
6470 if (CurrentContext()->has_extension()) {
6471 Handle<JSObject> extension =
6472 Handle<JSObject>(CurrentContext()->extension());
6473 if (extension->IsJSContextExtensionObject()) {
6474 extension->Print();
6475 }
6476 }
6477 break;
6478 }
6479
6480 default:
6481 UNREACHABLE();
6482 }
6483 PrintF("\n");
6484 }
6485#endif
6486
6487 private:
6488 JavaScriptFrame* frame_;
6489 Handle<JSFunction> function_;
6490 Handle<Context> context_;
6491 bool local_done_;
6492 bool at_local_;
6493
6494 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6495};
6496
6497
6498static Object* Runtime_GetScopeCount(Arguments args) {
6499 HandleScope scope;
6500 ASSERT(args.length() == 2);
6501
6502 // Check arguments.
6503 Object* check = Runtime_CheckExecutionState(args);
6504 if (check->IsFailure()) return check;
6505 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6506
6507 // Get the frame where the debugging is performed.
6508 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6509 JavaScriptFrameIterator it(id);
6510 JavaScriptFrame* frame = it.frame();
6511
6512 // Count the visible scopes.
6513 int n = 0;
6514 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6515 n++;
6516 }
6517
6518 return Smi::FromInt(n);
6519}
6520
6521
6522static const int kScopeDetailsTypeIndex = 0;
6523static const int kScopeDetailsObjectIndex = 1;
6524static const int kScopeDetailsSize = 2;
6525
6526// Return an array with scope details
6527// args[0]: number: break id
6528// args[1]: number: frame index
6529// args[2]: number: scope index
6530//
6531// The array returned contains the following information:
6532// 0: Scope type
6533// 1: Scope object
6534static Object* Runtime_GetScopeDetails(Arguments args) {
6535 HandleScope scope;
6536 ASSERT(args.length() == 3);
6537
6538 // Check arguments.
6539 Object* check = Runtime_CheckExecutionState(args);
6540 if (check->IsFailure()) return check;
6541 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6542 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6543
6544 // Get the frame where the debugging is performed.
6545 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6546 JavaScriptFrameIterator frame_it(id);
6547 JavaScriptFrame* frame = frame_it.frame();
6548
6549 // Find the requested scope.
6550 int n = 0;
6551 ScopeIterator it(frame);
6552 for (; !it.Done() && n < index; it.Next()) {
6553 n++;
6554 }
6555 if (it.Done()) {
6556 return Heap::undefined_value();
6557 }
6558
6559 // Calculate the size of the result.
6560 int details_size = kScopeDetailsSize;
6561 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6562
6563 // Fill in scope details.
6564 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6565 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6566
6567 return *Factory::NewJSArrayWithElements(details);
6568}
6569
6570
6571static Object* Runtime_DebugPrintScopes(Arguments args) {
6572 HandleScope scope;
6573 ASSERT(args.length() == 0);
6574
6575#ifdef DEBUG
6576 // Print the scopes for the top frame.
6577 StackFrameLocator locator;
6578 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6579 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6580 it.DebugPrint();
6581 }
6582#endif
6583 return Heap::undefined_value();
6584}
6585
6586
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006587static Object* Runtime_GetCFrames(Arguments args) {
6588 HandleScope scope;
6589 ASSERT(args.length() == 1);
6590 Object* result = Runtime_CheckExecutionState(args);
6591 if (result->IsFailure()) return result;
6592
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006593#if V8_HOST_ARCH_64_BIT
6594 UNIMPLEMENTED();
6595 return Heap::undefined_value();
6596#else
6597
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006598 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006599 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6600 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006601 if (frames_count == OS::kStackWalkError) {
6602 return Heap::undefined_value();
6603 }
6604
6605 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6606 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6607 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6608 for (int i = 0; i < frames_count; i++) {
6609 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6610 frame_value->SetProperty(
6611 *address_str,
6612 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6613 NONE);
6614
6615 // Get the stack walk text for this frame.
6616 Handle<String> frame_text;
6617 if (strlen(frames[i].text) > 0) {
6618 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6619 frame_text = Factory::NewStringFromAscii(str);
6620 }
6621
6622 if (!frame_text.is_null()) {
6623 frame_value->SetProperty(*text_str, *frame_text, NONE);
6624 }
6625
6626 frames_array->set(i, *frame_value);
6627 }
6628 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006629#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006630}
6631
6632
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006633static Object* Runtime_GetThreadCount(Arguments args) {
6634 HandleScope scope;
6635 ASSERT(args.length() == 1);
6636
6637 // Check arguments.
6638 Object* result = Runtime_CheckExecutionState(args);
6639 if (result->IsFailure()) return result;
6640
6641 // Count all archived V8 threads.
6642 int n = 0;
6643 for (ThreadState* thread = ThreadState::FirstInUse();
6644 thread != NULL;
6645 thread = thread->Next()) {
6646 n++;
6647 }
6648
6649 // Total number of threads is current thread and archived threads.
6650 return Smi::FromInt(n + 1);
6651}
6652
6653
6654static const int kThreadDetailsCurrentThreadIndex = 0;
6655static const int kThreadDetailsThreadIdIndex = 1;
6656static const int kThreadDetailsSize = 2;
6657
6658// Return an array with thread details
6659// args[0]: number: break id
6660// args[1]: number: thread index
6661//
6662// The array returned contains the following information:
6663// 0: Is current thread?
6664// 1: Thread id
6665static Object* Runtime_GetThreadDetails(Arguments args) {
6666 HandleScope scope;
6667 ASSERT(args.length() == 2);
6668
6669 // Check arguments.
6670 Object* check = Runtime_CheckExecutionState(args);
6671 if (check->IsFailure()) return check;
6672 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6673
6674 // Allocate array for result.
6675 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6676
6677 // Thread index 0 is current thread.
6678 if (index == 0) {
6679 // Fill the details.
6680 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6681 details->set(kThreadDetailsThreadIdIndex,
6682 Smi::FromInt(ThreadManager::CurrentId()));
6683 } else {
6684 // Find the thread with the requested index.
6685 int n = 1;
6686 ThreadState* thread = ThreadState::FirstInUse();
6687 while (index != n && thread != NULL) {
6688 thread = thread->Next();
6689 n++;
6690 }
6691 if (thread == NULL) {
6692 return Heap::undefined_value();
6693 }
6694
6695 // Fill the details.
6696 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6697 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6698 }
6699
6700 // Convert to JS array and return.
6701 return *Factory::NewJSArrayWithElements(details);
6702}
6703
6704
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006705static Object* Runtime_GetBreakLocations(Arguments args) {
6706 HandleScope scope;
6707 ASSERT(args.length() == 1);
6708
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006709 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6710 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006711 // Find the number of break points
6712 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6713 if (break_locations->IsUndefined()) return Heap::undefined_value();
6714 // Return array as JS array
6715 return *Factory::NewJSArrayWithElements(
6716 Handle<FixedArray>::cast(break_locations));
6717}
6718
6719
6720// Set a break point in a function
6721// args[0]: function
6722// args[1]: number: break source position (within the function source)
6723// args[2]: number: break point object
6724static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6725 HandleScope scope;
6726 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006727 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6728 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006729 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6730 RUNTIME_ASSERT(source_position >= 0);
6731 Handle<Object> break_point_object_arg = args.at<Object>(2);
6732
6733 // Set break point.
6734 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6735
6736 return Heap::undefined_value();
6737}
6738
6739
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006740Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6741 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006742 // Iterate the heap looking for SharedFunctionInfo generated from the
6743 // script. The inner most SharedFunctionInfo containing the source position
6744 // for the requested break point is found.
6745 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6746 // which is found is not compiled it is compiled and the heap is iterated
6747 // again as the compilation might create inner functions from the newly
6748 // compiled function and the actual requested break point might be in one of
6749 // these functions.
6750 bool done = false;
6751 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006752 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006753 Handle<SharedFunctionInfo> target;
6754 // The current candidate for the last function in script:
6755 Handle<SharedFunctionInfo> last;
6756 while (!done) {
6757 HeapIterator iterator;
6758 while (iterator.has_next()) {
6759 HeapObject* obj = iterator.next();
6760 ASSERT(obj != NULL);
6761 if (obj->IsSharedFunctionInfo()) {
6762 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6763 if (shared->script() == *script) {
6764 // If the SharedFunctionInfo found has the requested script data and
6765 // contains the source position it is a candidate.
6766 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006767 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006768 start_position = shared->start_position();
6769 }
6770 if (start_position <= position &&
6771 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006772 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006773 // candidate this is the new candidate.
6774 if (target.is_null()) {
6775 target_start_position = start_position;
6776 target = shared;
6777 } else {
6778 if (target_start_position < start_position &&
6779 shared->end_position() < target->end_position()) {
6780 target_start_position = start_position;
6781 target = shared;
6782 }
6783 }
6784 }
6785
6786 // Keep track of the last function in the script.
6787 if (last.is_null() ||
6788 shared->end_position() > last->start_position()) {
6789 last = shared;
6790 }
6791 }
6792 }
6793 }
6794
6795 // Make sure some candidate is selected.
6796 if (target.is_null()) {
6797 if (!last.is_null()) {
6798 // Position after the last function - use last.
6799 target = last;
6800 } else {
6801 // Unable to find function - possibly script without any function.
6802 return Heap::undefined_value();
6803 }
6804 }
6805
6806 // If the candidate found is compiled we are done. NOTE: when lazy
6807 // compilation of inner functions is introduced some additional checking
6808 // needs to be done here to compile inner functions.
6809 done = target->is_compiled();
6810 if (!done) {
6811 // If the candidate is not compiled compile it to reveal any inner
6812 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006813 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006814 }
6815 }
6816
6817 return *target;
6818}
6819
6820
6821// Change the state of a break point in a script. NOTE: Regarding performance
6822// see the NOTE for GetScriptFromScriptData.
6823// args[0]: script to set break point in
6824// args[1]: number: break source position (within the script source)
6825// args[2]: number: break point object
6826static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6827 HandleScope scope;
6828 ASSERT(args.length() == 3);
6829 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6830 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6831 RUNTIME_ASSERT(source_position >= 0);
6832 Handle<Object> break_point_object_arg = args.at<Object>(2);
6833
6834 // Get the script from the script wrapper.
6835 RUNTIME_ASSERT(wrapper->value()->IsScript());
6836 Handle<Script> script(Script::cast(wrapper->value()));
6837
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006838 Object* result = Runtime::FindSharedFunctionInfoInScript(
6839 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006840 if (!result->IsUndefined()) {
6841 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6842 // Find position within function. The script position might be before the
6843 // source position of the first function.
6844 int position;
6845 if (shared->start_position() > source_position) {
6846 position = 0;
6847 } else {
6848 position = source_position - shared->start_position();
6849 }
6850 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6851 }
6852 return Heap::undefined_value();
6853}
6854
6855
6856// Clear a break point
6857// args[0]: number: break point object
6858static Object* Runtime_ClearBreakPoint(Arguments args) {
6859 HandleScope scope;
6860 ASSERT(args.length() == 1);
6861 Handle<Object> break_point_object_arg = args.at<Object>(0);
6862
6863 // Clear break point.
6864 Debug::ClearBreakPoint(break_point_object_arg);
6865
6866 return Heap::undefined_value();
6867}
6868
6869
6870// Change the state of break on exceptions
6871// args[0]: boolean indicating uncaught exceptions
6872// args[1]: boolean indicating on/off
6873static Object* Runtime_ChangeBreakOnException(Arguments args) {
6874 HandleScope scope;
6875 ASSERT(args.length() == 2);
6876 ASSERT(args[0]->IsNumber());
6877 ASSERT(args[1]->IsBoolean());
6878
6879 // Update break point state
6880 ExceptionBreakType type =
6881 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6882 bool enable = args[1]->ToBoolean()->IsTrue();
6883 Debug::ChangeBreakOnException(type, enable);
6884 return Heap::undefined_value();
6885}
6886
6887
6888// Prepare for stepping
6889// args[0]: break id for checking execution state
6890// args[1]: step action from the enumeration StepAction
6891// args[2]: number of times to perform the step
6892static Object* Runtime_PrepareStep(Arguments args) {
6893 HandleScope scope;
6894 ASSERT(args.length() == 3);
6895 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006896 Object* check = Runtime_CheckExecutionState(args);
6897 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006898 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6899 return Top::Throw(Heap::illegal_argument_symbol());
6900 }
6901
6902 // Get the step action and check validity.
6903 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6904 if (step_action != StepIn &&
6905 step_action != StepNext &&
6906 step_action != StepOut &&
6907 step_action != StepInMin &&
6908 step_action != StepMin) {
6909 return Top::Throw(Heap::illegal_argument_symbol());
6910 }
6911
6912 // Get the number of steps.
6913 int step_count = NumberToInt32(args[2]);
6914 if (step_count < 1) {
6915 return Top::Throw(Heap::illegal_argument_symbol());
6916 }
6917
6918 // Prepare step.
6919 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6920 return Heap::undefined_value();
6921}
6922
6923
6924// Clear all stepping set by PrepareStep.
6925static Object* Runtime_ClearStepping(Arguments args) {
6926 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006927 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006928 Debug::ClearStepping();
6929 return Heap::undefined_value();
6930}
6931
6932
6933// Creates a copy of the with context chain. The copy of the context chain is
6934// is linked to the function context supplied.
6935static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6936 Handle<Context> function_context) {
6937 // At the bottom of the chain. Return the function context to link to.
6938 if (context_chain->is_function_context()) {
6939 return function_context;
6940 }
6941
6942 // Recursively copy the with contexts.
6943 Handle<Context> previous(context_chain->previous());
6944 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6945 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006946 CopyWithContextChain(function_context, previous),
6947 extension,
6948 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006949}
6950
6951
6952// Helper function to find or create the arguments object for
6953// Runtime_DebugEvaluate.
6954static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6955 Handle<JSFunction> function,
6956 Handle<Code> code,
6957 const ScopeInfo<>* sinfo,
6958 Handle<Context> function_context) {
6959 // Try to find the value of 'arguments' to pass as parameter. If it is not
6960 // found (that is the debugged function does not reference 'arguments' and
6961 // does not support eval) then create an 'arguments' object.
6962 int index;
6963 if (sinfo->number_of_stack_slots() > 0) {
6964 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6965 if (index != -1) {
6966 return Handle<Object>(frame->GetExpression(index));
6967 }
6968 }
6969
6970 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6971 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6972 NULL);
6973 if (index != -1) {
6974 return Handle<Object>(function_context->get(index));
6975 }
6976 }
6977
6978 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006979 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6980 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006981 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006982 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006983 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006984 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006985 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006986 return arguments;
6987}
6988
6989
6990// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006991// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006992// extension part has all the parameters and locals of the function on the
6993// stack frame. A function which calls eval with the code to evaluate is then
6994// compiled in this context and called in this context. As this context
6995// replaces the context of the function on the stack frame a new (empty)
6996// function is created as well to be used as the closure for the context.
6997// This function and the context acts as replacements for the function on the
6998// stack frame presenting the same view of the values of parameters and
6999// local variables as if the piece of JavaScript was evaluated at the point
7000// where the function on the stack frame is currently stopped.
7001static Object* Runtime_DebugEvaluate(Arguments args) {
7002 HandleScope scope;
7003
7004 // Check the execution state and decode arguments frame and source to be
7005 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007006 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007007 Object* check_result = Runtime_CheckExecutionState(args);
7008 if (check_result->IsFailure()) return check_result;
7009 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7010 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007011 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7012
7013 // Handle the processing of break.
7014 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007015
7016 // Get the frame where the debugging is performed.
7017 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7018 JavaScriptFrameIterator it(id);
7019 JavaScriptFrame* frame = it.frame();
7020 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7021 Handle<Code> code(function->code());
7022 ScopeInfo<> sinfo(*code);
7023
7024 // Traverse the saved contexts chain to find the active context for the
7025 // selected frame.
7026 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007027 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007028 save = save->prev();
7029 }
7030 ASSERT(save != NULL);
7031 SaveContext savex;
7032 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007033
7034 // Create the (empty) function replacing the function on the stack frame for
7035 // the purpose of evaluating in the context created below. It is important
7036 // that this function does not describe any parameters and local variables
7037 // in the context. If it does then this will cause problems with the lookup
7038 // in Context::Lookup, where context slots for parameters and local variables
7039 // are looked at before the extension object.
7040 Handle<JSFunction> go_between =
7041 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7042 go_between->set_context(function->context());
7043#ifdef DEBUG
7044 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7045 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7046 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7047#endif
7048
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007049 // Materialize the content of the local scope into a JSObject.
7050 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007051
7052 // Allocate a new context for the debug evaluation and set the extension
7053 // object build.
7054 Handle<Context> context =
7055 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007056 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007057 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007058 Handle<Context> frame_context(Context::cast(frame->context()));
7059 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007060 context = CopyWithContextChain(frame_context, context);
7061
7062 // Wrap the evaluation statement in a new function compiled in the newly
7063 // created context. The function has one parameter which has to be called
7064 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007065 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007066 // function(arguments,__source__) {return eval(__source__);}
7067 static const char* source_str =
7068 "function(arguments,__source__){return eval(__source__);}";
7069 static const int source_str_length = strlen(source_str);
7070 Handle<String> function_source =
7071 Factory::NewStringFromAscii(Vector<const char>(source_str,
7072 source_str_length));
7073 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007074 Compiler::CompileEval(function_source,
7075 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007076 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007077 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007078 if (boilerplate.is_null()) return Failure::Exception();
7079 Handle<JSFunction> compiled_function =
7080 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7081
7082 // Invoke the result of the compilation to get the evaluation function.
7083 bool has_pending_exception;
7084 Handle<Object> receiver(frame->receiver());
7085 Handle<Object> evaluation_function =
7086 Execution::Call(compiled_function, receiver, 0, NULL,
7087 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007088 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007089
7090 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7091 function_context);
7092
7093 // Invoke the evaluation function and return the result.
7094 const int argc = 2;
7095 Object** argv[argc] = { arguments.location(),
7096 Handle<Object>::cast(source).location() };
7097 Handle<Object> result =
7098 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7099 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007100 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007101
7102 // Skip the global proxy as it has no properties and always delegates to the
7103 // real global object.
7104 if (result->IsJSGlobalProxy()) {
7105 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7106 }
7107
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108 return *result;
7109}
7110
7111
7112static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7113 HandleScope scope;
7114
7115 // Check the execution state and decode arguments frame and source to be
7116 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007117 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007118 Object* check_result = Runtime_CheckExecutionState(args);
7119 if (check_result->IsFailure()) return check_result;
7120 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007121 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7122
7123 // Handle the processing of break.
7124 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007125
7126 // Enter the top context from before the debugger was invoked.
7127 SaveContext save;
7128 SaveContext* top = &save;
7129 while (top != NULL && *top->context() == *Debug::debug_context()) {
7130 top = top->prev();
7131 }
7132 if (top != NULL) {
7133 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007134 }
7135
7136 // Get the global context now set to the top context from before the
7137 // debugger was invoked.
7138 Handle<Context> context = Top::global_context();
7139
7140 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007141 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007142 Handle<JSFunction>(Compiler::CompileEval(source,
7143 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007144 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007145 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007146 if (boilerplate.is_null()) return Failure::Exception();
7147 Handle<JSFunction> compiled_function =
7148 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7149 context));
7150
7151 // Invoke the result of the compilation to get the evaluation function.
7152 bool has_pending_exception;
7153 Handle<Object> receiver = Top::global();
7154 Handle<Object> result =
7155 Execution::Call(compiled_function, receiver, 0, NULL,
7156 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007157 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007158 return *result;
7159}
7160
7161
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007162static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7163 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007164 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007165
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007166 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007167 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007168
7169 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007170 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007171 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7172 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7173 // because using
7174 // instances->set(i, *GetScriptWrapper(script))
7175 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7176 // already have deferenced the instances handle.
7177 Handle<JSValue> wrapper = GetScriptWrapper(script);
7178 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007179 }
7180
7181 // Return result as a JS array.
7182 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7183 Handle<JSArray>::cast(result)->SetContent(*instances);
7184 return *result;
7185}
7186
7187
7188// Helper function used by Runtime_DebugReferencedBy below.
7189static int DebugReferencedBy(JSObject* target,
7190 Object* instance_filter, int max_references,
7191 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007192 JSFunction* arguments_function) {
7193 NoHandleAllocation ha;
7194 AssertNoAllocation no_alloc;
7195
7196 // Iterate the heap.
7197 int count = 0;
7198 JSObject* last = NULL;
7199 HeapIterator iterator;
7200 while (iterator.has_next() &&
7201 (max_references == 0 || count < max_references)) {
7202 // Only look at all JSObjects.
7203 HeapObject* heap_obj = iterator.next();
7204 if (heap_obj->IsJSObject()) {
7205 // Skip context extension objects and argument arrays as these are
7206 // checked in the context of functions using them.
7207 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007208 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007209 obj->map()->constructor() == arguments_function) {
7210 continue;
7211 }
7212
7213 // Check if the JS object has a reference to the object looked for.
7214 if (obj->ReferencesObject(target)) {
7215 // Check instance filter if supplied. This is normally used to avoid
7216 // references from mirror objects (see Runtime_IsInPrototypeChain).
7217 if (!instance_filter->IsUndefined()) {
7218 Object* V = obj;
7219 while (true) {
7220 Object* prototype = V->GetPrototype();
7221 if (prototype->IsNull()) {
7222 break;
7223 }
7224 if (instance_filter == prototype) {
7225 obj = NULL; // Don't add this object.
7226 break;
7227 }
7228 V = prototype;
7229 }
7230 }
7231
7232 if (obj != NULL) {
7233 // Valid reference found add to instance array if supplied an update
7234 // count.
7235 if (instances != NULL && count < instances_size) {
7236 instances->set(count, obj);
7237 }
7238 last = obj;
7239 count++;
7240 }
7241 }
7242 }
7243 }
7244
7245 // Check for circular reference only. This can happen when the object is only
7246 // referenced from mirrors and has a circular reference in which case the
7247 // object is not really alive and would have been garbage collected if not
7248 // referenced from the mirror.
7249 if (count == 1 && last == target) {
7250 count = 0;
7251 }
7252
7253 // Return the number of referencing objects found.
7254 return count;
7255}
7256
7257
7258// Scan the heap for objects with direct references to an object
7259// args[0]: the object to find references to
7260// args[1]: constructor function for instances to exclude (Mirror)
7261// args[2]: the the maximum number of objects to return
7262static Object* Runtime_DebugReferencedBy(Arguments args) {
7263 ASSERT(args.length() == 3);
7264
7265 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007266 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007267
7268 // Check parameters.
7269 CONVERT_CHECKED(JSObject, target, args[0]);
7270 Object* instance_filter = args[1];
7271 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7272 instance_filter->IsJSObject());
7273 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7274 RUNTIME_ASSERT(max_references >= 0);
7275
7276 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007277 JSObject* arguments_boilerplate =
7278 Top::context()->global_context()->arguments_boilerplate();
7279 JSFunction* arguments_function =
7280 JSFunction::cast(arguments_boilerplate->map()->constructor());
7281
7282 // Get the number of referencing objects.
7283 int count;
7284 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007285 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007286
7287 // Allocate an array to hold the result.
7288 Object* object = Heap::AllocateFixedArray(count);
7289 if (object->IsFailure()) return object;
7290 FixedArray* instances = FixedArray::cast(object);
7291
7292 // Fill the referencing objects.
7293 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007294 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007295
7296 // Return result as JS array.
7297 Object* result =
7298 Heap::AllocateJSObject(
7299 Top::context()->global_context()->array_function());
7300 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7301 return result;
7302}
7303
7304
7305// Helper function used by Runtime_DebugConstructedBy below.
7306static int DebugConstructedBy(JSFunction* constructor, int max_references,
7307 FixedArray* instances, int instances_size) {
7308 AssertNoAllocation no_alloc;
7309
7310 // Iterate the heap.
7311 int count = 0;
7312 HeapIterator iterator;
7313 while (iterator.has_next() &&
7314 (max_references == 0 || count < max_references)) {
7315 // Only look at all JSObjects.
7316 HeapObject* heap_obj = iterator.next();
7317 if (heap_obj->IsJSObject()) {
7318 JSObject* obj = JSObject::cast(heap_obj);
7319 if (obj->map()->constructor() == constructor) {
7320 // Valid reference found add to instance array if supplied an update
7321 // count.
7322 if (instances != NULL && count < instances_size) {
7323 instances->set(count, obj);
7324 }
7325 count++;
7326 }
7327 }
7328 }
7329
7330 // Return the number of referencing objects found.
7331 return count;
7332}
7333
7334
7335// Scan the heap for objects constructed by a specific function.
7336// args[0]: the constructor to find instances of
7337// args[1]: the the maximum number of objects to return
7338static Object* Runtime_DebugConstructedBy(Arguments args) {
7339 ASSERT(args.length() == 2);
7340
7341 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007342 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007343
7344 // Check parameters.
7345 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7346 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7347 RUNTIME_ASSERT(max_references >= 0);
7348
7349 // Get the number of referencing objects.
7350 int count;
7351 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7352
7353 // Allocate an array to hold the result.
7354 Object* object = Heap::AllocateFixedArray(count);
7355 if (object->IsFailure()) return object;
7356 FixedArray* instances = FixedArray::cast(object);
7357
7358 // Fill the referencing objects.
7359 count = DebugConstructedBy(constructor, max_references, instances, count);
7360
7361 // Return result as JS array.
7362 Object* result =
7363 Heap::AllocateJSObject(
7364 Top::context()->global_context()->array_function());
7365 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7366 return result;
7367}
7368
7369
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007370// Find the effective prototype object as returned by __proto__.
7371// args[0]: the object to find the prototype for.
7372static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007373 ASSERT(args.length() == 1);
7374
7375 CONVERT_CHECKED(JSObject, obj, args[0]);
7376
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007377 // Use the __proto__ accessor.
7378 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007379}
7380
7381
7382static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007383 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007384 CPU::DebugBreak();
7385 return Heap::undefined_value();
7386}
7387
7388
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007389static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
7390#ifdef DEBUG
7391 HandleScope scope;
7392 ASSERT(args.length() == 1);
7393 // Get the function and make sure it is compiled.
7394 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7395 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7396 return Failure::Exception();
7397 }
7398 func->code()->PrintLn();
7399#endif // DEBUG
7400 return Heap::undefined_value();
7401}
ager@chromium.org9085a012009-05-11 19:22:57 +00007402
7403
7404static Object* Runtime_FunctionGetInferredName(Arguments args) {
7405 NoHandleAllocation ha;
7406 ASSERT(args.length() == 1);
7407
7408 CONVERT_CHECKED(JSFunction, f, args[0]);
7409 return f->shared()->inferred_name();
7410}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007411#endif // ENABLE_DEBUGGER_SUPPORT
7412
7413
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007414// Finds the script object from the script data. NOTE: This operation uses
7415// heap traversal to find the function generated for the source position
7416// for the requested break point. For lazily compiled functions several heap
7417// traversals might be required rendering this operation as a rather slow
7418// operation. However for setting break points which is normally done through
7419// some kind of user interaction the performance is not crucial.
7420static Handle<Object> Runtime_GetScriptFromScriptName(
7421 Handle<String> script_name) {
7422 // Scan the heap for Script objects to find the script with the requested
7423 // script data.
7424 Handle<Script> script;
7425 HeapIterator iterator;
7426 while (script.is_null() && iterator.has_next()) {
7427 HeapObject* obj = iterator.next();
7428 // If a script is found check if it has the script data requested.
7429 if (obj->IsScript()) {
7430 if (Script::cast(obj)->name()->IsString()) {
7431 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7432 script = Handle<Script>(Script::cast(obj));
7433 }
7434 }
7435 }
7436 }
7437
7438 // If no script with the requested script data is found return undefined.
7439 if (script.is_null()) return Factory::undefined_value();
7440
7441 // Return the script found.
7442 return GetScriptWrapper(script);
7443}
7444
7445
7446// Get the script object from script data. NOTE: Regarding performance
7447// see the NOTE for GetScriptFromScriptData.
7448// args[0]: script data for the script to find the source for
7449static Object* Runtime_GetScript(Arguments args) {
7450 HandleScope scope;
7451
7452 ASSERT(args.length() == 1);
7453
7454 CONVERT_CHECKED(String, script_name, args[0]);
7455
7456 // Find the requested script.
7457 Handle<Object> result =
7458 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7459 return *result;
7460}
7461
7462
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007463// Determines whether the given stack frame should be displayed in
7464// a stack trace. The caller is the error constructor that asked
7465// for the stack trace to be collected. The first time a construct
7466// call to this function is encountered it is skipped. The seen_caller
7467// in/out parameter is used to remember if the caller has been seen
7468// yet.
7469static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7470 bool* seen_caller) {
7471 // Only display JS frames.
7472 if (!raw_frame->is_java_script())
7473 return false;
7474 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7475 Object* raw_fun = frame->function();
7476 // Not sure when this can happen but skip it just in case.
7477 if (!raw_fun->IsJSFunction())
7478 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007479 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007480 *seen_caller = true;
7481 return false;
7482 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007483 // Skip all frames until we've seen the caller. Also, skip the most
7484 // obvious builtin calls. Some builtin calls (such as Number.ADD
7485 // which is invoked using 'call') are very difficult to recognize
7486 // so we're leaving them in for now.
7487 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007488}
7489
7490
7491// Collect the raw data for a stack trace. Returns an array of three
7492// element segments each containing a receiver, function and native
7493// code offset.
7494static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007495 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007496 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007497 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7498
7499 HandleScope scope;
7500
7501 int initial_size = limit < 10 ? limit : 10;
7502 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007503
7504 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007505 // If the caller parameter is a function we skip frames until we're
7506 // under it before starting to collect.
7507 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007508 int cursor = 0;
7509 int frames_seen = 0;
7510 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007511 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007512 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007513 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007514 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007515 Object* recv = frame->receiver();
7516 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007517 Address pc = frame->pc();
7518 Address start = frame->code()->address();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007519 Smi* offset = Smi::FromInt(pc - start);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007520 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007521 if (cursor + 2 < elements->length()) {
7522 elements->set(cursor++, recv);
7523 elements->set(cursor++, fun);
7524 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7525 } else {
7526 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007527 Handle<Object> recv_handle(recv);
7528 Handle<Object> fun_handle(fun);
7529 SetElement(result, cursor++, recv_handle);
7530 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007531 SetElement(result, cursor++, Handle<Smi>(offset));
7532 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007533 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007534 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007535 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007536
7537 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7538
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007539 return *result;
7540}
7541
7542
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007543static Object* Runtime_Abort(Arguments args) {
7544 ASSERT(args.length() == 2);
7545 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7546 Smi::cast(args[1])->value());
7547 Top::PrintStack();
7548 OS::Abort();
7549 UNREACHABLE();
7550 return NULL;
7551}
7552
7553
kasper.lund44510672008-07-25 07:37:58 +00007554#ifdef DEBUG
7555// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7556// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007557static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007558 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007559 HandleScope scope;
7560 Handle<JSArray> result = Factory::NewJSArray(0);
7561 int index = 0;
7562#define ADD_ENTRY(Name, argc) \
7563 { \
7564 HandleScope inner; \
7565 Handle<String> name = \
7566 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7567 Handle<JSArray> pair = Factory::NewJSArray(0); \
7568 SetElement(pair, 0, name); \
7569 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7570 SetElement(result, index++, pair); \
7571 }
7572 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7573#undef ADD_ENTRY
7574 return *result;
7575}
kasper.lund44510672008-07-25 07:37:58 +00007576#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007577
7578
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007579static Object* Runtime_Log(Arguments args) {
7580 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007581 CONVERT_CHECKED(String, format, args[0]);
7582 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007583 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007584 Logger::LogRuntime(chars, elms);
7585 return Heap::undefined_value();
7586}
7587
7588
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007589static Object* Runtime_IS_VAR(Arguments args) {
7590 UNREACHABLE(); // implemented as macro in the parser
7591 return NULL;
7592}
7593
7594
7595// ----------------------------------------------------------------------------
7596// Implementation of Runtime
7597
7598#define F(name, nargs) \
7599 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7600 static_cast<int>(Runtime::k##name) },
7601
7602static Runtime::Function Runtime_functions[] = {
7603 RUNTIME_FUNCTION_LIST(F)
7604 { NULL, NULL, NULL, 0, -1 }
7605};
7606
7607#undef F
7608
7609
7610Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7611 ASSERT(0 <= fid && fid < kNofFunctions);
7612 return &Runtime_functions[fid];
7613}
7614
7615
7616Runtime::Function* Runtime::FunctionForName(const char* name) {
7617 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7618 if (strcmp(f->name, name) == 0) {
7619 return f;
7620 }
7621 }
7622 return NULL;
7623}
7624
7625
7626void Runtime::PerformGC(Object* result) {
7627 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007628 if (failure->IsRetryAfterGC()) {
7629 // Try to do a garbage collection; ignore it if it fails. The C
7630 // entry stub will throw an out-of-memory exception in that case.
7631 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7632 } else {
7633 // Handle last resort GC and make sure to allow future allocations
7634 // to grow the heap without causing GCs (if possible).
7635 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007636 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007637 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007638}
7639
7640
7641} } // namespace v8::internal