blob: 617dfaadeffb1115b6ce0ff6780554f441174980 [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"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000037#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038#include "debug.h"
39#include "execution.h"
40#include "jsregexp.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000041#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042#include "platform.h"
43#include "runtime.h"
44#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000045#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000046#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000047#include "v8threads.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.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 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
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000401static Object* Runtime_CreateObjectLiteral(Arguments args) {
402 HandleScope scope;
403 ASSERT(args.length() == 3);
404 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
405 CONVERT_SMI_CHECKED(literals_index, args[1]);
406 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
407
408 // Check if boilerplate exists. If not, create it first.
409 Handle<Object> boilerplate(literals->get(literals_index));
410 if (*boilerplate == Heap::undefined_value()) {
411 boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
412 if (boilerplate.is_null()) return Failure::Exception();
413 // Update the functions literal and return the boilerplate.
414 literals->set(literals_index, *boilerplate);
415 }
416 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
417}
418
419
420static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
421 HandleScope scope;
422 ASSERT(args.length() == 3);
423 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
424 CONVERT_SMI_CHECKED(literals_index, args[1]);
425 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
426
427 // Check if boilerplate exists. If not, create it first.
428 Handle<Object> boilerplate(literals->get(literals_index));
429 if (*boilerplate == Heap::undefined_value()) {
430 boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
431 if (boilerplate.is_null()) return Failure::Exception();
432 // Update the functions literal and return the boilerplate.
433 literals->set(literals_index, *boilerplate);
434 }
435 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
436}
437
438
439static Object* Runtime_CreateArrayLiteral(Arguments args) {
440 HandleScope scope;
441 ASSERT(args.length() == 3);
442 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
443 CONVERT_SMI_CHECKED(literals_index, args[1]);
444 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
445
446 // Check if boilerplate exists. If not, create it first.
447 Handle<Object> boilerplate(literals->get(literals_index));
448 if (*boilerplate == Heap::undefined_value()) {
449 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
450 if (boilerplate.is_null()) return Failure::Exception();
451 // Update the functions literal and return the boilerplate.
452 literals->set(literals_index, *boilerplate);
453 }
454 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
455}
456
457
458static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
459 HandleScope scope;
460 ASSERT(args.length() == 3);
461 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
462 CONVERT_SMI_CHECKED(literals_index, args[1]);
463 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
464
465 // Check if boilerplate exists. If not, create it first.
466 Handle<Object> boilerplate(literals->get(literals_index));
467 if (*boilerplate == Heap::undefined_value()) {
468 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
469 if (boilerplate.is_null()) return Failure::Exception();
470 // Update the functions literal and return the boilerplate.
471 literals->set(literals_index, *boilerplate);
472 }
473 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
474}
475
476
ager@chromium.org32912102009-01-16 10:38:43 +0000477static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
478 ASSERT(args.length() == 2);
479 CONVERT_CHECKED(String, key, args[0]);
480 Object* value = args[1];
481 // Create a catch context extension object.
482 JSFunction* constructor =
483 Top::context()->global_context()->context_extension_function();
484 Object* object = Heap::AllocateJSObject(constructor);
485 if (object->IsFailure()) return object;
486 // Assign the exception value to the catch variable and make sure
487 // that the catch variable is DontDelete.
488 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
489 if (value->IsFailure()) return value;
490 return object;
491}
492
493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000494static Object* Runtime_ClassOf(Arguments args) {
495 NoHandleAllocation ha;
496 ASSERT(args.length() == 1);
497 Object* obj = args[0];
498 if (!obj->IsJSObject()) return Heap::null_value();
499 return JSObject::cast(obj)->class_name();
500}
501
ager@chromium.org7c537e22008-10-16 08:43:32 +0000502
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000503static Object* Runtime_IsInPrototypeChain(Arguments args) {
504 NoHandleAllocation ha;
505 ASSERT(args.length() == 2);
506 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
507 Object* O = args[0];
508 Object* V = args[1];
509 while (true) {
510 Object* prototype = V->GetPrototype();
511 if (prototype->IsNull()) return Heap::false_value();
512 if (O == prototype) return Heap::true_value();
513 V = prototype;
514 }
515}
516
517
ager@chromium.org9085a012009-05-11 19:22:57 +0000518// Inserts an object as the hidden prototype of another object.
519static Object* Runtime_SetHiddenPrototype(Arguments args) {
520 NoHandleAllocation ha;
521 ASSERT(args.length() == 2);
522 CONVERT_CHECKED(JSObject, jsobject, args[0]);
523 CONVERT_CHECKED(JSObject, proto, args[1]);
524
525 // Sanity checks. The old prototype (that we are replacing) could
526 // theoretically be null, but if it is not null then check that we
527 // didn't already install a hidden prototype here.
528 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
529 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
530 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
531
532 // Allocate up front before we start altering state in case we get a GC.
533 Object* map_or_failure = proto->map()->CopyDropTransitions();
534 if (map_or_failure->IsFailure()) return map_or_failure;
535 Map* new_proto_map = Map::cast(map_or_failure);
536
537 map_or_failure = jsobject->map()->CopyDropTransitions();
538 if (map_or_failure->IsFailure()) return map_or_failure;
539 Map* new_map = Map::cast(map_or_failure);
540
541 // Set proto's prototype to be the old prototype of the object.
542 new_proto_map->set_prototype(jsobject->GetPrototype());
543 proto->set_map(new_proto_map);
544 new_proto_map->set_is_hidden_prototype();
545
546 // Set the object's prototype to proto.
547 new_map->set_prototype(proto);
548 jsobject->set_map(new_map);
549
550 return Heap::undefined_value();
551}
552
553
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000554static Object* Runtime_IsConstructCall(Arguments args) {
555 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000556 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000557 JavaScriptFrameIterator it;
558 return Heap::ToBoolean(it.frame()->IsConstructor());
559}
560
561
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000562// Recursively traverses hidden prototypes if property is not found
563static void GetOwnPropertyImplementation(JSObject* obj,
564 String* name,
565 LookupResult* result) {
566 obj->LocalLookupRealNamedProperty(name, result);
567
568 if (!result->IsProperty()) {
569 Object* proto = obj->GetPrototype();
570 if (proto->IsJSObject() &&
571 JSObject::cast(proto)->map()->is_hidden_prototype())
572 GetOwnPropertyImplementation(JSObject::cast(proto),
573 name, result);
574 }
575}
576
577
578// Returns an array with the property description:
579// if args[1] is not a property on args[0]
580// returns undefined
581// if args[1] is a data property on args[0]
582// [false, value, Writeable, Enumerable, Configurable]
583// if args[1] is an accessor on args[0]
584// [true, GetFunction, SetFunction, Enumerable, Configurable]
585static Object* Runtime_GetOwnProperty(Arguments args) {
586 HandleScope scope;
587 Handle<FixedArray> elms = Factory::NewFixedArray(5);
588 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
589 LookupResult result;
590 CONVERT_CHECKED(JSObject, obj, args[0]);
591 CONVERT_CHECKED(String, name, args[1]);
592
593 // Use recursive implementation to also traverse hidden prototypes
594 GetOwnPropertyImplementation(obj, name, &result);
595
596 if (!result.IsProperty())
597 return Heap::undefined_value();
598
599 if (result.type() == CALLBACKS) {
600 Object* structure = result.GetCallbackObject();
601 if (structure->IsProxy()) {
602 // Property that is internally implemented as a callback.
603 Object* value = obj->GetPropertyWithCallback(
604 obj, structure, name, result.holder());
605 elms->set(0, Heap::false_value());
606 elms->set(1, value);
607 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
608 } else if (structure->IsFixedArray()) {
609 // __defineGetter__/__defineSetter__ callback.
610 elms->set(0, Heap::true_value());
611 elms->set(1, FixedArray::cast(structure)->get(0));
612 elms->set(2, FixedArray::cast(structure)->get(1));
613 } else {
614 // TODO(ricow): Handle API callbacks.
615 return Heap::undefined_value();
616 }
617 } else {
618 elms->set(0, Heap::false_value());
619 elms->set(1, result.GetLazyValue());
620 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
621 }
622
623 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
624 elms->set(4, Heap::ToBoolean(!result.IsReadOnly()));
625 return *desc;
626}
627
628
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000629static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000630 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000631 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000632 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
633 CONVERT_ARG_CHECKED(String, pattern, 1);
634 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000635 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
636 if (result.is_null()) return Failure::Exception();
637 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000638}
639
640
641static Object* Runtime_CreateApiFunction(Arguments args) {
642 HandleScope scope;
643 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000644 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000645 return *Factory::CreateApiFunction(data);
646}
647
648
649static Object* Runtime_IsTemplate(Arguments args) {
650 ASSERT(args.length() == 1);
651 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000652 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000653 return Heap::ToBoolean(result);
654}
655
656
657static Object* Runtime_GetTemplateField(Arguments args) {
658 ASSERT(args.length() == 2);
659 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000660 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000661 int index = field->value();
662 int offset = index * kPointerSize + HeapObject::kHeaderSize;
663 InstanceType type = templ->map()->instance_type();
664 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
665 type == OBJECT_TEMPLATE_INFO_TYPE);
666 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000667 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000668 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
669 } else {
670 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
671 }
672 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000673}
674
675
ager@chromium.org870a0b62008-11-04 11:43:05 +0000676static Object* Runtime_DisableAccessChecks(Arguments args) {
677 ASSERT(args.length() == 1);
678 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000679 Map* old_map = object->map();
680 bool needs_access_checks = old_map->is_access_check_needed();
681 if (needs_access_checks) {
682 // Copy map so it won't interfere constructor's initial map.
683 Object* new_map = old_map->CopyDropTransitions();
684 if (new_map->IsFailure()) return new_map;
685
686 Map::cast(new_map)->set_is_access_check_needed(false);
687 object->set_map(Map::cast(new_map));
688 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000689 return needs_access_checks ? Heap::true_value() : Heap::false_value();
690}
691
692
693static Object* Runtime_EnableAccessChecks(Arguments args) {
694 ASSERT(args.length() == 1);
695 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000696 Map* old_map = object->map();
697 if (!old_map->is_access_check_needed()) {
698 // Copy map so it won't interfere constructor's initial map.
699 Object* new_map = old_map->CopyDropTransitions();
700 if (new_map->IsFailure()) return new_map;
701
702 Map::cast(new_map)->set_is_access_check_needed(true);
703 object->set_map(Map::cast(new_map));
704 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000705 return Heap::undefined_value();
706}
707
708
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000709static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
710 HandleScope scope;
711 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
712 Handle<Object> args[2] = { type_handle, name };
713 Handle<Object> error =
714 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
715 return Top::Throw(*error);
716}
717
718
719static Object* Runtime_DeclareGlobals(Arguments args) {
720 HandleScope scope;
721 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
722
ager@chromium.org3811b432009-10-28 14:53:37 +0000723 Handle<Context> context = args.at<Context>(0);
724 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725 bool is_eval = Smi::cast(args[2])->value() == 1;
726
727 // Compute the property attributes. According to ECMA-262, section
728 // 13, page 71, the property must be read-only and
729 // non-deletable. However, neither SpiderMonkey nor KJS creates the
730 // property as read-only, so we don't either.
731 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
732
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733 // Traverse the name/value pairs and set the properties.
734 int length = pairs->length();
735 for (int i = 0; i < length; i += 2) {
736 HandleScope scope;
737 Handle<String> name(String::cast(pairs->get(i)));
738 Handle<Object> value(pairs->get(i + 1));
739
740 // We have to declare a global const property. To capture we only
741 // assign to it when evaluating the assignment for "const x =
742 // <expr>" the initial value is the hole.
743 bool is_const_property = value->IsTheHole();
744
745 if (value->IsUndefined() || is_const_property) {
746 // Lookup the property in the global object, and don't set the
747 // value of the variable if the property is already there.
748 LookupResult lookup;
749 global->Lookup(*name, &lookup);
750 if (lookup.IsProperty()) {
751 // Determine if the property is local by comparing the holder
752 // against the global object. The information will be used to
753 // avoid throwing re-declaration errors when declaring
754 // variables or constants that exist in the prototype chain.
755 bool is_local = (*global == lookup.holder());
756 // Get the property attributes and determine if the property is
757 // read-only.
758 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
759 bool is_read_only = (attributes & READ_ONLY) != 0;
760 if (lookup.type() == INTERCEPTOR) {
761 // If the interceptor says the property is there, we
762 // just return undefined without overwriting the property.
763 // Otherwise, we continue to setting the property.
764 if (attributes != ABSENT) {
765 // Check if the existing property conflicts with regards to const.
766 if (is_local && (is_read_only || is_const_property)) {
767 const char* type = (is_read_only) ? "const" : "var";
768 return ThrowRedeclarationError(type, name);
769 };
770 // The property already exists without conflicting: Go to
771 // the next declaration.
772 continue;
773 }
774 // Fall-through and introduce the absent property by using
775 // SetProperty.
776 } else {
777 if (is_local && (is_read_only || is_const_property)) {
778 const char* type = (is_read_only) ? "const" : "var";
779 return ThrowRedeclarationError(type, name);
780 }
781 // The property already exists without conflicting: Go to
782 // the next declaration.
783 continue;
784 }
785 }
786 } else {
787 // Copy the function and update its context. Use it as value.
788 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
789 Handle<JSFunction> function =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +0000790 Factory::NewFunctionFromBoilerplate(boilerplate, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000791 value = function;
792 }
793
794 LookupResult lookup;
795 global->LocalLookup(*name, &lookup);
796
797 PropertyAttributes attributes = is_const_property
798 ? static_cast<PropertyAttributes>(base | READ_ONLY)
799 : base;
800
801 if (lookup.IsProperty()) {
802 // There's a local property that we need to overwrite because
803 // we're either declaring a function or there's an interceptor
804 // that claims the property is absent.
805
806 // Check for conflicting re-declarations. We cannot have
807 // conflicting types in case of intercepted properties because
808 // they are absent.
809 if (lookup.type() != INTERCEPTOR &&
810 (lookup.IsReadOnly() || is_const_property)) {
811 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
812 return ThrowRedeclarationError(type, name);
813 }
814 SetProperty(global, name, value, attributes);
815 } else {
816 // If a property with this name does not already exist on the
817 // global object add the property locally. We take special
818 // precautions to always add it as a local property even in case
819 // of callbacks in the prototype chain (this rules out using
820 // SetProperty). Also, we must use the handle-based version to
821 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000822 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000823 }
824 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000825
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000826 return Heap::undefined_value();
827}
828
829
830static Object* Runtime_DeclareContextSlot(Arguments args) {
831 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000832 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833
ager@chromium.org7c537e22008-10-16 08:43:32 +0000834 CONVERT_ARG_CHECKED(Context, context, 0);
835 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000836 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000837 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000838 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000839 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840
841 // Declarations are always done in the function context.
842 context = Handle<Context>(context->fcontext());
843
844 int index;
845 PropertyAttributes attributes;
846 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000847 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000848 context->Lookup(name, flags, &index, &attributes);
849
850 if (attributes != ABSENT) {
851 // The name was declared before; check for conflicting
852 // re-declarations: This is similar to the code in parser.cc in
853 // the AstBuildingParser::Declare function.
854 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
855 // Functions are not read-only.
856 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
857 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
858 return ThrowRedeclarationError(type, name);
859 }
860
861 // Initialize it if necessary.
862 if (*initial_value != NULL) {
863 if (index >= 0) {
864 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000865 // the function context or the arguments object.
866 if (holder->IsContext()) {
867 ASSERT(holder.is_identical_to(context));
868 if (((attributes & READ_ONLY) == 0) ||
869 context->get(index)->IsTheHole()) {
870 context->set(index, *initial_value);
871 }
872 } else {
873 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000874 }
875 } else {
876 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000877 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 SetProperty(context_ext, name, initial_value, mode);
879 }
880 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000883 // The property is not in the function context. It needs to be
884 // "declared" in the function context's extension context, or in the
885 // global context.
886 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000887 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000888 // The function context's extension context exists - use it.
889 context_ext = Handle<JSObject>(context->extension());
890 } else {
891 // The function context's extension context does not exists - allocate
892 // it.
893 context_ext = Factory::NewJSObject(Top::context_extension_function());
894 // And store it in the extension slot.
895 context->set_extension(*context_ext);
896 }
897 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000898
ager@chromium.org7c537e22008-10-16 08:43:32 +0000899 // Declare the property by setting it to the initial value if provided,
900 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
901 // constant declarations).
902 ASSERT(!context_ext->HasLocalProperty(*name));
903 Handle<Object> value(Heap::undefined_value());
904 if (*initial_value != NULL) value = initial_value;
905 SetProperty(context_ext, name, value, mode);
906 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
907 }
908
909 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000910}
911
912
913static Object* Runtime_InitializeVarGlobal(Arguments args) {
914 NoHandleAllocation nha;
915
916 // Determine if we need to assign to the variable if it already
917 // exists (based on the number of arguments).
918 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
919 bool assign = args.length() == 2;
920
921 CONVERT_ARG_CHECKED(String, name, 0);
922 GlobalObject* global = Top::context()->global();
923
924 // According to ECMA-262, section 12.2, page 62, the property must
925 // not be deletable.
926 PropertyAttributes attributes = DONT_DELETE;
927
928 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000929 // there, there is a property with this name in the prototype chain.
930 // We follow Safari and Firefox behavior and only set the property
931 // locally if there is an explicit initialization value that we have
932 // to assign to the property. When adding the property we take
933 // special precautions to always add it as a local property even in
934 // case of callbacks in the prototype chain (this rules out using
935 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
936 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000937 // Note that objects can have hidden prototypes, so we need to traverse
938 // the whole chain of hidden prototypes to do a 'local' lookup.
939 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000940 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000941 while (true) {
942 real_holder->LocalLookup(*name, &lookup);
943 if (lookup.IsProperty()) {
944 // Determine if this is a redeclaration of something read-only.
945 if (lookup.IsReadOnly()) {
946 // If we found readonly property on one of hidden prototypes,
947 // just shadow it.
948 if (real_holder != Top::context()->global()) break;
949 return ThrowRedeclarationError("const", name);
950 }
951
952 // Determine if this is a redeclaration of an intercepted read-only
953 // property and figure out if the property exists at all.
954 bool found = true;
955 PropertyType type = lookup.type();
956 if (type == INTERCEPTOR) {
957 HandleScope handle_scope;
958 Handle<JSObject> holder(real_holder);
959 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
960 real_holder = *holder;
961 if (intercepted == ABSENT) {
962 // The interceptor claims the property isn't there. We need to
963 // make sure to introduce it.
964 found = false;
965 } else if ((intercepted & READ_ONLY) != 0) {
966 // The property is present, but read-only. Since we're trying to
967 // overwrite it with a variable declaration we must throw a
968 // re-declaration error. However if we found readonly property
969 // on one of hidden prototypes, just shadow it.
970 if (real_holder != Top::context()->global()) break;
971 return ThrowRedeclarationError("const", name);
972 }
973 }
974
975 if (found && !assign) {
976 // The global property is there and we're not assigning any value
977 // to it. Just return.
978 return Heap::undefined_value();
979 }
980
981 // Assign the value (or undefined) to the property.
982 Object* value = (assign) ? args[1] : Heap::undefined_value();
983 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000984 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000985
986 Object* proto = real_holder->GetPrototype();
987 if (!proto->IsJSObject())
988 break;
989
990 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
991 break;
992
993 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000994 }
995
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000996 global = Top::context()->global();
997 if (assign) {
998 return global->IgnoreAttributesAndSetLocalProperty(*name,
999 args[1],
1000 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001002 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001003}
1004
1005
1006static Object* Runtime_InitializeConstGlobal(Arguments args) {
1007 // All constants are declared with an initial value. The name
1008 // of the constant is the first argument and the initial value
1009 // is the second.
1010 RUNTIME_ASSERT(args.length() == 2);
1011 CONVERT_ARG_CHECKED(String, name, 0);
1012 Handle<Object> value = args.at<Object>(1);
1013
1014 // Get the current global object from top.
1015 GlobalObject* global = Top::context()->global();
1016
1017 // According to ECMA-262, section 12.2, page 62, the property must
1018 // not be deletable. Since it's a const, it must be READ_ONLY too.
1019 PropertyAttributes attributes =
1020 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1021
1022 // Lookup the property locally in the global object. If it isn't
1023 // there, we add the property and take special precautions to always
1024 // add it as a local property even in case of callbacks in the
1025 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001026 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001027 LookupResult lookup;
1028 global->LocalLookup(*name, &lookup);
1029 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030 return global->IgnoreAttributesAndSetLocalProperty(*name,
1031 *value,
1032 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001033 }
1034
1035 // Determine if this is a redeclaration of something not
1036 // read-only. In case the result is hidden behind an interceptor we
1037 // need to ask it for the property attributes.
1038 if (!lookup.IsReadOnly()) {
1039 if (lookup.type() != INTERCEPTOR) {
1040 return ThrowRedeclarationError("var", name);
1041 }
1042
1043 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1044
1045 // Throw re-declaration error if the intercepted property is present
1046 // but not read-only.
1047 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1048 return ThrowRedeclarationError("var", name);
1049 }
1050
1051 // Restore global object from context (in case of GC) and continue
1052 // with setting the value because the property is either absent or
1053 // read-only. We also have to do redo the lookup.
1054 global = Top::context()->global();
1055
1056 // BUG 1213579: Handle the case where we have to set a read-only
1057 // property through an interceptor and only do it if it's
1058 // uninitialized, e.g. the hole. Nirk...
1059 global->SetProperty(*name, *value, attributes);
1060 return *value;
1061 }
1062
1063 // Set the value, but only we're assigning the initial value to a
1064 // constant. For now, we determine this by checking if the
1065 // current value is the hole.
1066 PropertyType type = lookup.type();
1067 if (type == FIELD) {
1068 FixedArray* properties = global->properties();
1069 int index = lookup.GetFieldIndex();
1070 if (properties->get(index)->IsTheHole()) {
1071 properties->set(index, *value);
1072 }
1073 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001074 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1075 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001076 }
1077 } else {
1078 // Ignore re-initialization of constants that have already been
1079 // assigned a function value.
1080 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1081 }
1082
1083 // Use the set value as the result of the operation.
1084 return *value;
1085}
1086
1087
1088static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1089 HandleScope scope;
1090 ASSERT(args.length() == 3);
1091
1092 Handle<Object> value(args[0]);
1093 ASSERT(!value->IsTheHole());
1094 CONVERT_ARG_CHECKED(Context, context, 1);
1095 Handle<String> name(String::cast(args[2]));
1096
1097 // Initializations are always done in the function context.
1098 context = Handle<Context>(context->fcontext());
1099
1100 int index;
1101 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001102 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001103 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001104 context->Lookup(name, flags, &index, &attributes);
1105
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001106 // In most situations, the property introduced by the const
1107 // declaration should be present in the context extension object.
1108 // However, because declaration and initialization are separate, the
1109 // property might have been deleted (if it was introduced by eval)
1110 // before we reach the initialization point.
1111 //
1112 // Example:
1113 //
1114 // function f() { eval("delete x; const x;"); }
1115 //
1116 // In that case, the initialization behaves like a normal assignment
1117 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001118 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001119 // Property was found in a context.
1120 if (holder->IsContext()) {
1121 // The holder cannot be the function context. If it is, there
1122 // should have been a const redeclaration error when declaring
1123 // the const property.
1124 ASSERT(!holder.is_identical_to(context));
1125 if ((attributes & READ_ONLY) == 0) {
1126 Handle<Context>::cast(holder)->set(index, *value);
1127 }
1128 } else {
1129 // The holder is an arguments object.
1130 ASSERT((attributes & READ_ONLY) == 0);
1131 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001132 }
1133 return *value;
1134 }
1135
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001136 // The property could not be found, we introduce it in the global
1137 // context.
1138 if (attributes == ABSENT) {
1139 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1140 SetProperty(global, name, value, NONE);
1141 return *value;
1142 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001143
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001144 // The property was present in a context extension object.
1145 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001146
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001147 if (*context_ext == context->extension()) {
1148 // This is the property that was introduced by the const
1149 // declaration. Set it if it hasn't been set before. NOTE: We
1150 // cannot use GetProperty() to get the current value as it
1151 // 'unholes' the value.
1152 LookupResult lookup;
1153 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1154 ASSERT(lookup.IsProperty()); // the property was declared
1155 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1156
1157 PropertyType type = lookup.type();
1158 if (type == FIELD) {
1159 FixedArray* properties = context_ext->properties();
1160 int index = lookup.GetFieldIndex();
1161 if (properties->get(index)->IsTheHole()) {
1162 properties->set(index, *value);
1163 }
1164 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001165 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1166 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001167 }
1168 } else {
1169 // We should not reach here. Any real, named property should be
1170 // either a field or a dictionary slot.
1171 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001172 }
1173 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001174 // The property was found in a different context extension object.
1175 // Set it if it is not a read-only property.
1176 if ((attributes & READ_ONLY) == 0) {
1177 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1178 // Setting a property might throw an exception. Exceptions
1179 // are converted to empty handles in handle operations. We
1180 // need to convert back to exceptions here.
1181 if (set.is_null()) {
1182 ASSERT(Top::has_pending_exception());
1183 return Failure::Exception();
1184 }
1185 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001186 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001187
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001188 return *value;
1189}
1190
1191
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001192static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1193 Arguments args) {
1194 HandleScope scope;
1195 ASSERT(args.length() == 2);
1196 CONVERT_ARG_CHECKED(JSObject, object, 0);
1197 CONVERT_SMI_CHECKED(properties, args[1]);
1198 if (object->HasFastProperties()) {
1199 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1200 }
1201 return *object;
1202}
1203
1204
1205static Object* Runtime_TransformToFastProperties(Arguments args) {
1206 HandleScope scope;
1207 ASSERT(args.length() == 1);
1208 CONVERT_ARG_CHECKED(JSObject, object, 0);
1209 if (!object->HasFastProperties() && !object->IsGlobalObject()) {
1210 TransformToFastProperties(object, 0);
1211 }
1212 return *object;
1213}
1214
1215
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001216static Object* Runtime_RegExpExec(Arguments args) {
1217 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001218 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001219 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1220 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001221 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001222 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001223 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001224 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001225 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001226 RUNTIME_ASSERT(index >= 0);
1227 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001228 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001229 Handle<Object> result = RegExpImpl::Exec(regexp,
1230 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001231 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001232 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001233 if (result.is_null()) return Failure::Exception();
1234 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001235}
1236
1237
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001238static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1239 HandleScope scope;
1240 ASSERT(args.length() == 4);
1241 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1242 int index = Smi::cast(args[1])->value();
1243 Handle<String> pattern = args.at<String>(2);
1244 Handle<String> flags = args.at<String>(3);
1245
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001246 // Get the RegExp function from the context in the literals array.
1247 // This is the RegExp function from the context in which the
1248 // function was created. We do not use the RegExp function from the
1249 // current global context because this might be the RegExp function
1250 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001251 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001252 Handle<JSFunction>(
1253 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001254 // Compute the regular expression literal.
1255 bool has_pending_exception;
1256 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001257 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1258 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001259 if (has_pending_exception) {
1260 ASSERT(Top::has_pending_exception());
1261 return Failure::Exception();
1262 }
1263 literals->set(index, *regexp);
1264 return *regexp;
1265}
1266
1267
1268static Object* Runtime_FunctionGetName(Arguments args) {
1269 NoHandleAllocation ha;
1270 ASSERT(args.length() == 1);
1271
1272 CONVERT_CHECKED(JSFunction, f, args[0]);
1273 return f->shared()->name();
1274}
1275
1276
ager@chromium.org236ad962008-09-25 09:45:57 +00001277static Object* Runtime_FunctionSetName(Arguments args) {
1278 NoHandleAllocation ha;
1279 ASSERT(args.length() == 2);
1280
1281 CONVERT_CHECKED(JSFunction, f, args[0]);
1282 CONVERT_CHECKED(String, name, args[1]);
1283 f->shared()->set_name(name);
1284 return Heap::undefined_value();
1285}
1286
1287
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001288static Object* Runtime_FunctionGetScript(Arguments args) {
1289 HandleScope scope;
1290 ASSERT(args.length() == 1);
1291
1292 CONVERT_CHECKED(JSFunction, fun, args[0]);
1293 Handle<Object> script = Handle<Object>(fun->shared()->script());
1294 if (!script->IsScript()) return Heap::undefined_value();
1295
1296 return *GetScriptWrapper(Handle<Script>::cast(script));
1297}
1298
1299
1300static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1301 NoHandleAllocation ha;
1302 ASSERT(args.length() == 1);
1303
1304 CONVERT_CHECKED(JSFunction, f, args[0]);
1305 return f->shared()->GetSourceCode();
1306}
1307
1308
1309static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1310 NoHandleAllocation ha;
1311 ASSERT(args.length() == 1);
1312
1313 CONVERT_CHECKED(JSFunction, fun, args[0]);
1314 int pos = fun->shared()->start_position();
1315 return Smi::FromInt(pos);
1316}
1317
1318
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001319static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1320 ASSERT(args.length() == 2);
1321
1322 CONVERT_CHECKED(JSFunction, fun, args[0]);
1323 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1324
1325 Code* code = fun->code();
1326 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1327
1328 Address pc = code->address() + offset;
1329 return Smi::FromInt(fun->code()->SourcePosition(pc));
1330}
1331
1332
1333
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001334static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1335 NoHandleAllocation ha;
1336 ASSERT(args.length() == 2);
1337
1338 CONVERT_CHECKED(JSFunction, fun, args[0]);
1339 CONVERT_CHECKED(String, name, args[1]);
1340 fun->SetInstanceClassName(name);
1341 return Heap::undefined_value();
1342}
1343
1344
1345static Object* Runtime_FunctionSetLength(Arguments args) {
1346 NoHandleAllocation ha;
1347 ASSERT(args.length() == 2);
1348
1349 CONVERT_CHECKED(JSFunction, fun, args[0]);
1350 CONVERT_CHECKED(Smi, length, args[1]);
1351 fun->shared()->set_length(length->value());
1352 return length;
1353}
1354
1355
1356static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001357 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001358 ASSERT(args.length() == 2);
1359
1360 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001361 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1362 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001363 return args[0]; // return TOS
1364}
1365
1366
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001367static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1368 NoHandleAllocation ha;
1369 ASSERT(args.length() == 1);
1370
1371 CONVERT_CHECKED(JSFunction, f, args[0]);
1372 // The function_data field of the shared function info is used exclusively by
1373 // the API.
1374 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1375 : Heap::false_value();
1376}
1377
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001378static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1379 NoHandleAllocation ha;
1380 ASSERT(args.length() == 1);
1381
1382 CONVERT_CHECKED(JSFunction, f, args[0]);
1383 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1384}
1385
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001386
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001387static Object* Runtime_SetCode(Arguments args) {
1388 HandleScope scope;
1389 ASSERT(args.length() == 2);
1390
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001391 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001392 Handle<Object> code = args.at<Object>(1);
1393
1394 Handle<Context> context(target->context());
1395
1396 if (!code->IsNull()) {
1397 RUNTIME_ASSERT(code->IsJSFunction());
1398 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1399 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1400 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1401 return Failure::Exception();
1402 }
1403 // Set the code, formal parameter count, and the length of the target
1404 // function.
1405 target->set_code(fun->code());
1406 target->shared()->set_length(fun->shared()->length());
1407 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001408 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001409 // Set the source code of the target function to undefined.
1410 // SetCode is only used for built-in constructors like String,
1411 // Array, and Object, and some web code
1412 // doesn't like seeing source code for constructors.
1413 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001414 // Clear the optimization hints related to the compiled code as these are no
1415 // longer valid when the code is overwritten.
1416 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001417 context = Handle<Context>(fun->context());
1418
1419 // Make sure we get a fresh copy of the literal vector to avoid
1420 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001421 int number_of_literals = fun->NumberOfLiterals();
1422 Handle<FixedArray> literals =
1423 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001424 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001425 // Insert the object, regexp and array functions in the literals
1426 // array prefix. These are the functions that will be used when
1427 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001428 literals->set(JSFunction::kLiteralGlobalContextIndex,
1429 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001430 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001431 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001432 }
1433
1434 target->set_context(*context);
1435 return *target;
1436}
1437
1438
1439static Object* CharCodeAt(String* subject, Object* index) {
1440 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001441 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001442 // Flatten the string. If someone wants to get a char at an index
1443 // in a cons string, it is likely that more indices will be
1444 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001445 Object* flat = subject->TryFlatten();
1446 if (flat->IsFailure()) return flat;
1447 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001448 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001449 return Heap::nan_value();
1450 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001451 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001452}
1453
1454
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001455static Object* CharFromCode(Object* char_code) {
1456 uint32_t code;
1457 if (Array::IndexFromObject(char_code, &code)) {
1458 if (code <= 0xffff) {
1459 return Heap::LookupSingleCharacterStringFromCode(code);
1460 }
1461 }
1462 return Heap::empty_string();
1463}
1464
1465
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001466static Object* Runtime_StringCharCodeAt(Arguments args) {
1467 NoHandleAllocation ha;
1468 ASSERT(args.length() == 2);
1469
1470 CONVERT_CHECKED(String, subject, args[0]);
1471 Object* index = args[1];
1472 return CharCodeAt(subject, index);
1473}
1474
1475
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001476static Object* Runtime_StringCharAt(Arguments args) {
1477 NoHandleAllocation ha;
1478 ASSERT(args.length() == 2);
1479
1480 CONVERT_CHECKED(String, subject, args[0]);
1481 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001482 Object* code = CharCodeAt(subject, index);
1483 if (code == Heap::nan_value()) {
1484 return Heap::undefined_value();
1485 }
1486 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001487}
1488
1489
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001490static Object* Runtime_CharFromCode(Arguments args) {
1491 NoHandleAllocation ha;
1492 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001493 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001494}
1495
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001496// Forward declarations.
1497static const int kStringBuilderConcatHelperLengthBits = 11;
1498static const int kStringBuilderConcatHelperPositionBits = 19;
1499
1500template <typename schar>
1501static inline void StringBuilderConcatHelper(String*,
1502 schar*,
1503 FixedArray*,
1504 int);
1505
1506typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1507typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1508
1509class ReplacementStringBuilder {
1510 public:
1511 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1512 : subject_(subject),
1513 parts_(Factory::NewFixedArray(estimated_part_count)),
1514 part_count_(0),
1515 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001516 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001517 // Require a non-zero initial size. Ensures that doubling the size to
1518 // extend the array will work.
1519 ASSERT(estimated_part_count > 0);
1520 }
1521
1522 void EnsureCapacity(int elements) {
1523 int length = parts_->length();
1524 int required_length = part_count_ + elements;
1525 if (length < required_length) {
1526 int new_length = length;
1527 do {
1528 new_length *= 2;
1529 } while (new_length < required_length);
1530 Handle<FixedArray> extended_array =
1531 Factory::NewFixedArray(new_length);
1532 parts_->CopyTo(0, *extended_array, 0, part_count_);
1533 parts_ = extended_array;
1534 }
1535 }
1536
1537 void AddSubjectSlice(int from, int to) {
1538 ASSERT(from >= 0);
1539 int length = to - from;
1540 ASSERT(length > 0);
1541 // Can we encode the slice in 11 bits for length and 19 bits for
1542 // start position - as used by StringBuilderConcatHelper?
1543 if (StringBuilderSubstringLength::is_valid(length) &&
1544 StringBuilderSubstringPosition::is_valid(from)) {
1545 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1546 StringBuilderSubstringPosition::encode(from);
1547 AddElement(Smi::FromInt(encoded_slice));
1548 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001549 // Otherwise encode as two smis.
1550 AddElement(Smi::FromInt(-length));
1551 AddElement(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001552 }
1553 IncrementCharacterCount(length);
1554 }
1555
1556
1557 void AddString(Handle<String> string) {
1558 int length = string->length();
1559 ASSERT(length > 0);
1560 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001561 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001562 is_ascii_ = false;
1563 }
1564 IncrementCharacterCount(length);
1565 }
1566
1567
1568 Handle<String> ToString() {
1569 if (part_count_ == 0) {
1570 return Factory::empty_string();
1571 }
1572
1573 Handle<String> joined_string;
1574 if (is_ascii_) {
1575 joined_string = NewRawAsciiString(character_count_);
1576 AssertNoAllocation no_alloc;
1577 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1578 char* char_buffer = seq->GetChars();
1579 StringBuilderConcatHelper(*subject_,
1580 char_buffer,
1581 *parts_,
1582 part_count_);
1583 } else {
1584 // Non-ASCII.
1585 joined_string = NewRawTwoByteString(character_count_);
1586 AssertNoAllocation no_alloc;
1587 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1588 uc16* char_buffer = seq->GetChars();
1589 StringBuilderConcatHelper(*subject_,
1590 char_buffer,
1591 *parts_,
1592 part_count_);
1593 }
1594 return joined_string;
1595 }
1596
1597
1598 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001599 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001600 V8::FatalProcessOutOfMemory("String.replace result too large.");
1601 }
1602 character_count_ += by;
1603 }
1604
1605 private:
1606
1607 Handle<String> NewRawAsciiString(int size) {
1608 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1609 }
1610
1611
1612 Handle<String> NewRawTwoByteString(int size) {
1613 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1614 }
1615
1616
1617 void AddElement(Object* element) {
1618 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001619 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001620 parts_->set(part_count_, element);
1621 part_count_++;
1622 }
1623
1624 Handle<String> subject_;
1625 Handle<FixedArray> parts_;
1626 int part_count_;
1627 int character_count_;
1628 bool is_ascii_;
1629};
1630
1631
1632class CompiledReplacement {
1633 public:
1634 CompiledReplacement()
1635 : parts_(1), replacement_substrings_(0) {}
1636
1637 void Compile(Handle<String> replacement,
1638 int capture_count,
1639 int subject_length);
1640
1641 void Apply(ReplacementStringBuilder* builder,
1642 int match_from,
1643 int match_to,
1644 Handle<JSArray> last_match_info);
1645
1646 // Number of distinct parts of the replacement pattern.
1647 int parts() {
1648 return parts_.length();
1649 }
1650 private:
1651 enum PartType {
1652 SUBJECT_PREFIX = 1,
1653 SUBJECT_SUFFIX,
1654 SUBJECT_CAPTURE,
1655 REPLACEMENT_SUBSTRING,
1656 REPLACEMENT_STRING,
1657
1658 NUMBER_OF_PART_TYPES
1659 };
1660
1661 struct ReplacementPart {
1662 static inline ReplacementPart SubjectMatch() {
1663 return ReplacementPart(SUBJECT_CAPTURE, 0);
1664 }
1665 static inline ReplacementPart SubjectCapture(int capture_index) {
1666 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1667 }
1668 static inline ReplacementPart SubjectPrefix() {
1669 return ReplacementPart(SUBJECT_PREFIX, 0);
1670 }
1671 static inline ReplacementPart SubjectSuffix(int subject_length) {
1672 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1673 }
1674 static inline ReplacementPart ReplacementString() {
1675 return ReplacementPart(REPLACEMENT_STRING, 0);
1676 }
1677 static inline ReplacementPart ReplacementSubString(int from, int to) {
1678 ASSERT(from >= 0);
1679 ASSERT(to > from);
1680 return ReplacementPart(-from, to);
1681 }
1682
1683 // If tag <= 0 then it is the negation of a start index of a substring of
1684 // the replacement pattern, otherwise it's a value from PartType.
1685 ReplacementPart(int tag, int data)
1686 : tag(tag), data(data) {
1687 // Must be non-positive or a PartType value.
1688 ASSERT(tag < NUMBER_OF_PART_TYPES);
1689 }
1690 // Either a value of PartType or a non-positive number that is
1691 // the negation of an index into the replacement string.
1692 int tag;
1693 // The data value's interpretation depends on the value of tag:
1694 // tag == SUBJECT_PREFIX ||
1695 // tag == SUBJECT_SUFFIX: data is unused.
1696 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1697 // tag == REPLACEMENT_SUBSTRING ||
1698 // tag == REPLACEMENT_STRING: data is index into array of substrings
1699 // of the replacement string.
1700 // tag <= 0: Temporary representation of the substring of the replacement
1701 // string ranging over -tag .. data.
1702 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1703 // substring objects.
1704 int data;
1705 };
1706
1707 template<typename Char>
1708 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1709 Vector<Char> characters,
1710 int capture_count,
1711 int subject_length) {
1712 int length = characters.length();
1713 int last = 0;
1714 for (int i = 0; i < length; i++) {
1715 Char c = characters[i];
1716 if (c == '$') {
1717 int next_index = i + 1;
1718 if (next_index == length) { // No next character!
1719 break;
1720 }
1721 Char c2 = characters[next_index];
1722 switch (c2) {
1723 case '$':
1724 if (i > last) {
1725 // There is a substring before. Include the first "$".
1726 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1727 last = next_index + 1; // Continue after the second "$".
1728 } else {
1729 // Let the next substring start with the second "$".
1730 last = next_index;
1731 }
1732 i = next_index;
1733 break;
1734 case '`':
1735 if (i > last) {
1736 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1737 }
1738 parts->Add(ReplacementPart::SubjectPrefix());
1739 i = next_index;
1740 last = i + 1;
1741 break;
1742 case '\'':
1743 if (i > last) {
1744 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1745 }
1746 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1747 i = next_index;
1748 last = i + 1;
1749 break;
1750 case '&':
1751 if (i > last) {
1752 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1753 }
1754 parts->Add(ReplacementPart::SubjectMatch());
1755 i = next_index;
1756 last = i + 1;
1757 break;
1758 case '0':
1759 case '1':
1760 case '2':
1761 case '3':
1762 case '4':
1763 case '5':
1764 case '6':
1765 case '7':
1766 case '8':
1767 case '9': {
1768 int capture_ref = c2 - '0';
1769 if (capture_ref > capture_count) {
1770 i = next_index;
1771 continue;
1772 }
1773 int second_digit_index = next_index + 1;
1774 if (second_digit_index < length) {
1775 // Peek ahead to see if we have two digits.
1776 Char c3 = characters[second_digit_index];
1777 if ('0' <= c3 && c3 <= '9') { // Double digits.
1778 int double_digit_ref = capture_ref * 10 + c3 - '0';
1779 if (double_digit_ref <= capture_count) {
1780 next_index = second_digit_index;
1781 capture_ref = double_digit_ref;
1782 }
1783 }
1784 }
1785 if (capture_ref > 0) {
1786 if (i > last) {
1787 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1788 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001789 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001790 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1791 last = next_index + 1;
1792 }
1793 i = next_index;
1794 break;
1795 }
1796 default:
1797 i = next_index;
1798 break;
1799 }
1800 }
1801 }
1802 if (length > last) {
1803 if (last == 0) {
1804 parts->Add(ReplacementPart::ReplacementString());
1805 } else {
1806 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1807 }
1808 }
1809 }
1810
1811 ZoneList<ReplacementPart> parts_;
1812 ZoneList<Handle<String> > replacement_substrings_;
1813};
1814
1815
1816void CompiledReplacement::Compile(Handle<String> replacement,
1817 int capture_count,
1818 int subject_length) {
1819 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001820 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001821 AssertNoAllocation no_alloc;
1822 ParseReplacementPattern(&parts_,
1823 replacement->ToAsciiVector(),
1824 capture_count,
1825 subject_length);
1826 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001827 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001828 AssertNoAllocation no_alloc;
1829
1830 ParseReplacementPattern(&parts_,
1831 replacement->ToUC16Vector(),
1832 capture_count,
1833 subject_length);
1834 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001835 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001836 int substring_index = 0;
1837 for (int i = 0, n = parts_.length(); i < n; i++) {
1838 int tag = parts_[i].tag;
1839 if (tag <= 0) { // A replacement string slice.
1840 int from = -tag;
1841 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001842 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001843 parts_[i].tag = REPLACEMENT_SUBSTRING;
1844 parts_[i].data = substring_index;
1845 substring_index++;
1846 } else if (tag == REPLACEMENT_STRING) {
1847 replacement_substrings_.Add(replacement);
1848 parts_[i].data = substring_index;
1849 substring_index++;
1850 }
1851 }
1852}
1853
1854
1855void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1856 int match_from,
1857 int match_to,
1858 Handle<JSArray> last_match_info) {
1859 for (int i = 0, n = parts_.length(); i < n; i++) {
1860 ReplacementPart part = parts_[i];
1861 switch (part.tag) {
1862 case SUBJECT_PREFIX:
1863 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1864 break;
1865 case SUBJECT_SUFFIX: {
1866 int subject_length = part.data;
1867 if (match_to < subject_length) {
1868 builder->AddSubjectSlice(match_to, subject_length);
1869 }
1870 break;
1871 }
1872 case SUBJECT_CAPTURE: {
1873 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001874 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001875 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1876 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1877 if (from >= 0 && to > from) {
1878 builder->AddSubjectSlice(from, to);
1879 }
1880 break;
1881 }
1882 case REPLACEMENT_SUBSTRING:
1883 case REPLACEMENT_STRING:
1884 builder->AddString(replacement_substrings_[part.data]);
1885 break;
1886 default:
1887 UNREACHABLE();
1888 }
1889 }
1890}
1891
1892
1893
1894static Object* StringReplaceRegExpWithString(String* subject,
1895 JSRegExp* regexp,
1896 String* replacement,
1897 JSArray* last_match_info) {
1898 ASSERT(subject->IsFlat());
1899 ASSERT(replacement->IsFlat());
1900
1901 HandleScope handles;
1902
1903 int length = subject->length();
1904 Handle<String> subject_handle(subject);
1905 Handle<JSRegExp> regexp_handle(regexp);
1906 Handle<String> replacement_handle(replacement);
1907 Handle<JSArray> last_match_info_handle(last_match_info);
1908 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1909 subject_handle,
1910 0,
1911 last_match_info_handle);
1912 if (match.is_null()) {
1913 return Failure::Exception();
1914 }
1915 if (match->IsNull()) {
1916 return *subject_handle;
1917 }
1918
1919 int capture_count = regexp_handle->CaptureCount();
1920
1921 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001922 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001923 CompiledReplacement compiled_replacement;
1924 compiled_replacement.Compile(replacement_handle,
1925 capture_count,
1926 length);
1927
1928 bool is_global = regexp_handle->GetFlags().is_global();
1929
1930 // Guessing the number of parts that the final result string is built
1931 // from. Global regexps can match any number of times, so we guess
1932 // conservatively.
1933 int expected_parts =
1934 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1935 ReplacementStringBuilder builder(subject_handle, expected_parts);
1936
1937 // Index of end of last match.
1938 int prev = 0;
1939
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001940 // Number of parts added by compiled replacement plus preceeding
1941 // string and possibly suffix after last match. It is possible for
1942 // all components to use two elements when encoded as two smis.
1943 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001944 bool matched = true;
1945 do {
1946 ASSERT(last_match_info_handle->HasFastElements());
1947 // Increase the capacity of the builder before entering local handle-scope,
1948 // so its internal buffer can safely allocate a new handle if it grows.
1949 builder.EnsureCapacity(parts_added_per_loop);
1950
1951 HandleScope loop_scope;
1952 int start, end;
1953 {
1954 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001955 FixedArray* match_info_array =
1956 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001957
1958 ASSERT_EQ(capture_count * 2 + 2,
1959 RegExpImpl::GetLastCaptureCount(match_info_array));
1960 start = RegExpImpl::GetCapture(match_info_array, 0);
1961 end = RegExpImpl::GetCapture(match_info_array, 1);
1962 }
1963
1964 if (prev < start) {
1965 builder.AddSubjectSlice(prev, start);
1966 }
1967 compiled_replacement.Apply(&builder,
1968 start,
1969 end,
1970 last_match_info_handle);
1971 prev = end;
1972
1973 // Only continue checking for global regexps.
1974 if (!is_global) break;
1975
1976 // Continue from where the match ended, unless it was an empty match.
1977 int next = end;
1978 if (start == end) {
1979 next = end + 1;
1980 if (next > length) break;
1981 }
1982
1983 match = RegExpImpl::Exec(regexp_handle,
1984 subject_handle,
1985 next,
1986 last_match_info_handle);
1987 if (match.is_null()) {
1988 return Failure::Exception();
1989 }
1990 matched = !match->IsNull();
1991 } while (matched);
1992
1993 if (prev < length) {
1994 builder.AddSubjectSlice(prev, length);
1995 }
1996
1997 return *(builder.ToString());
1998}
1999
2000
2001static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2002 ASSERT(args.length() == 4);
2003
2004 CONVERT_CHECKED(String, subject, args[0]);
2005 if (!subject->IsFlat()) {
2006 Object* flat_subject = subject->TryFlatten();
2007 if (flat_subject->IsFailure()) {
2008 return flat_subject;
2009 }
2010 subject = String::cast(flat_subject);
2011 }
2012
2013 CONVERT_CHECKED(String, replacement, args[2]);
2014 if (!replacement->IsFlat()) {
2015 Object* flat_replacement = replacement->TryFlatten();
2016 if (flat_replacement->IsFailure()) {
2017 return flat_replacement;
2018 }
2019 replacement = String::cast(flat_replacement);
2020 }
2021
2022 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2023 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2024
2025 ASSERT(last_match_info->HasFastElements());
2026
2027 return StringReplaceRegExpWithString(subject,
2028 regexp,
2029 replacement,
2030 last_match_info);
2031}
2032
2033
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002034
ager@chromium.org7c537e22008-10-16 08:43:32 +00002035// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2036// limit, we can fix the size of tables.
2037static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002038// Reduce alphabet to this size.
2039static const int kBMAlphabetSize = 0x100;
2040// For patterns below this length, the skip length of Boyer-Moore is too short
2041// to compensate for the algorithmic overhead compared to simple brute force.
2042static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002043
ager@chromium.org7c537e22008-10-16 08:43:32 +00002044// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2045// shift. Only allows the last kBMMaxShift characters of the needle
2046// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002047class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002048 public:
2049 BMGoodSuffixBuffers() {}
2050 inline void init(int needle_length) {
2051 ASSERT(needle_length > 1);
2052 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2053 int len = needle_length - start;
2054 biased_suffixes_ = suffixes_ - start;
2055 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2056 for (int i = 0; i <= len; i++) {
2057 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002058 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002059 }
2060 inline int& suffix(int index) {
2061 ASSERT(biased_suffixes_ + index >= suffixes_);
2062 return biased_suffixes_[index];
2063 }
2064 inline int& shift(int index) {
2065 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2066 return biased_good_suffix_shift_[index];
2067 }
2068 private:
2069 int suffixes_[kBMMaxShift + 1];
2070 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002071 int* biased_suffixes_;
2072 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002073 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2074};
2075
2076// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002077static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002078static BMGoodSuffixBuffers bmgs_buffers;
2079
2080// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002081template <typename pchar>
2082static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
2083 int start) {
2084 // Run forwards to populate bad_char_table, so that *last* instance
2085 // of character equivalence class is the one registered.
2086 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002087 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2088 : kBMAlphabetSize;
2089 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002090 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002091 } else {
2092 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002093 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002094 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002095 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002096 for (int i = start; i < pattern.length() - 1; i++) {
2097 pchar c = pattern[i];
2098 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002099 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002100 }
2101}
2102
2103template <typename pchar>
2104static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002105 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002106 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002107 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002108 // Compute Good Suffix tables.
2109 bmgs_buffers.init(m);
2110
2111 bmgs_buffers.shift(m-1) = 1;
2112 bmgs_buffers.suffix(m) = m + 1;
2113 pchar last_char = pattern[m - 1];
2114 int suffix = m + 1;
2115 for (int i = m; i > start;) {
2116 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2117 if (bmgs_buffers.shift(suffix) == len) {
2118 bmgs_buffers.shift(suffix) = suffix - i;
2119 }
2120 suffix = bmgs_buffers.suffix(suffix);
2121 }
2122 i--;
2123 suffix--;
2124 bmgs_buffers.suffix(i) = suffix;
2125 if (suffix == m) {
2126 // No suffix to extend, so we check against last_char only.
2127 while (i > start && pattern[i - 1] != last_char) {
2128 if (bmgs_buffers.shift(m) == len) {
2129 bmgs_buffers.shift(m) = m - i;
2130 }
2131 i--;
2132 bmgs_buffers.suffix(i) = m;
2133 }
2134 if (i > start) {
2135 i--;
2136 suffix--;
2137 bmgs_buffers.suffix(i) = suffix;
2138 }
2139 }
2140 }
2141 if (suffix < m) {
2142 for (int i = start; i <= m; i++) {
2143 if (bmgs_buffers.shift(i) == len) {
2144 bmgs_buffers.shift(i) = suffix - start;
2145 }
2146 if (i == suffix) {
2147 suffix = bmgs_buffers.suffix(suffix);
2148 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002149 }
2150 }
2151}
2152
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002153template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002154static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002155 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002156 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002157 }
2158 if (sizeof(pchar) == 1) {
2159 if (char_code > String::kMaxAsciiCharCode) {
2160 return -1;
2161 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002162 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002163 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002164 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002165}
2166
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002167// Restricted simplified Boyer-Moore string matching.
2168// Uses only the bad-shift table of Boyer-Moore and only uses it
2169// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002170template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002171static int BoyerMooreHorspool(Vector<const schar> subject,
2172 Vector<const pchar> pattern,
2173 int start_index,
2174 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002175 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002176 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002177 // Only preprocess at most kBMMaxShift last characters of pattern.
2178 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002179
ager@chromium.org7c537e22008-10-16 08:43:32 +00002180 BoyerMoorePopulateBadCharTable(pattern, start);
2181
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002182 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002183 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002184 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002185 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002186 // Perform search
2187 for (idx = start_index; idx <= n - m;) {
2188 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002189 int c;
2190 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002191 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002192 int shift = j - bc_occ;
2193 idx += shift;
2194 badness += 1 - shift; // at most zero, so badness cannot increase.
2195 if (idx > n - m) {
2196 *complete = true;
2197 return -1;
2198 }
2199 }
2200 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002201 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002202 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002203 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002204 return idx;
2205 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002206 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002207 // Badness increases by the number of characters we have
2208 // checked, and decreases by the number of characters we
2209 // can skip by shifting. It's a measure of how we are doing
2210 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002211 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002212 if (badness > 0) {
2213 *complete = false;
2214 return idx;
2215 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002216 }
2217 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002218 *complete = true;
2219 return -1;
2220}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002221
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002222
2223template <typename schar, typename pchar>
2224static int BoyerMooreIndexOf(Vector<const schar> subject,
2225 Vector<const pchar> pattern,
2226 int idx) {
2227 int n = subject.length();
2228 int m = pattern.length();
2229 // Only preprocess at most kBMMaxShift last characters of pattern.
2230 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2231
2232 // Build the Good Suffix table and continue searching.
2233 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2234 pchar last_char = pattern[m - 1];
2235 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002236 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002237 int j = m - 1;
2238 schar c;
2239 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002240 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002241 idx += shift;
2242 if (idx > n - m) {
2243 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002244 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002245 }
2246 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2247 if (j < 0) {
2248 return idx;
2249 } else if (j < start) {
2250 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002251 // Fall back on BMH shift.
2252 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002253 } else {
2254 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002255 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002256 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002257 if (gs_shift > shift) {
2258 shift = gs_shift;
2259 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002260 idx += shift;
2261 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002262 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002263
2264 return -1;
2265}
2266
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002267
2268template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002269static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002270 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002271 int start_index) {
2272 for (int i = start_index, n = string.length(); i < n; i++) {
2273 if (pattern_char == string[i]) {
2274 return i;
2275 }
2276 }
2277 return -1;
2278}
2279
2280// Trivial string search for shorter strings.
2281// On return, if "complete" is set to true, the return value is the
2282// final result of searching for the patter in the subject.
2283// If "complete" is set to false, the return value is the index where
2284// further checking should start, i.e., it's guaranteed that the pattern
2285// does not occur at a position prior to the returned index.
2286template <typename pchar, typename schar>
2287static int SimpleIndexOf(Vector<const schar> subject,
2288 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002289 int idx,
2290 bool* complete) {
2291 // Badness is a count of how much work we have done. When we have
2292 // done enough work we decide it's probably worth switching to a better
2293 // algorithm.
2294 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002295 // We know our pattern is at least 2 characters, we cache the first so
2296 // the common case of the first character not matching is faster.
2297 pchar pattern_first_char = pattern[0];
2298
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002299 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2300 badness++;
2301 if (badness > 0) {
2302 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002303 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002304 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002305 if (subject[i] != pattern_first_char) continue;
2306 int j = 1;
2307 do {
2308 if (pattern[j] != subject[i+j]) {
2309 break;
2310 }
2311 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002312 } while (j < pattern.length());
2313 if (j == pattern.length()) {
2314 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002315 return i;
2316 }
2317 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002318 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002319 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002320 return -1;
2321}
2322
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002323// Simple indexOf that never bails out. For short patterns only.
2324template <typename pchar, typename schar>
2325static int SimpleIndexOf(Vector<const schar> subject,
2326 Vector<const pchar> pattern,
2327 int idx) {
2328 pchar pattern_first_char = pattern[0];
2329 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2330 if (subject[i] != pattern_first_char) continue;
2331 int j = 1;
2332 do {
2333 if (pattern[j] != subject[i+j]) {
2334 break;
2335 }
2336 j++;
2337 } while (j < pattern.length());
2338 if (j == pattern.length()) {
2339 return i;
2340 }
2341 }
2342 return -1;
2343}
2344
2345
2346// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002347template <typename schar, typename pchar>
2348static int StringMatchStrategy(Vector<const schar> sub,
2349 Vector<const pchar> pat,
2350 int start_index) {
2351 ASSERT(pat.length() > 1);
2352
2353 // We have an ASCII haystack and a non-ASCII needle. Check if there
2354 // really is a non-ASCII character in the needle and bail out if there
2355 // is.
2356 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2357 for (int i = 0; i < pat.length(); i++) {
2358 uc16 c = pat[i];
2359 if (c > String::kMaxAsciiCharCode) {
2360 return -1;
2361 }
2362 }
2363 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002364 if (pat.length() < kBMMinPatternLength) {
2365 // We don't believe fancy searching can ever be more efficient.
2366 // The max shift of Boyer-Moore on a pattern of this length does
2367 // not compensate for the overhead.
2368 return SimpleIndexOf(sub, pat, start_index);
2369 }
2370 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002371 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002372 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2373 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002374 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002375 if (complete) return idx;
2376 return BoyerMooreIndexOf(sub, pat, idx);
2377}
2378
2379// Perform string match of pattern on subject, starting at start index.
2380// Caller must ensure that 0 <= start_index <= sub->length(),
2381// and should check that pat->length() + start_index <= sub->length()
2382int Runtime::StringMatch(Handle<String> sub,
2383 Handle<String> pat,
2384 int start_index) {
2385 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002386 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002387
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002388 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002389 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002390
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002391 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002392 if (start_index + pattern_length > subject_length) return -1;
2393
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002394 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002395 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002396 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002397 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002398 // character patterns linear search is necessary, so any smart
2399 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002400 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002401 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002402 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002403 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002404 if (pchar > String::kMaxAsciiCharCode) {
2405 return -1;
2406 }
2407 Vector<const char> ascii_vector =
2408 sub->ToAsciiVector().SubVector(start_index, subject_length);
2409 const void* pos = memchr(ascii_vector.start(),
2410 static_cast<const char>(pchar),
2411 static_cast<size_t>(ascii_vector.length()));
2412 if (pos == NULL) {
2413 return -1;
2414 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002415 return static_cast<int>(reinterpret_cast<const char*>(pos)
2416 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002417 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002418 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002419 }
2420
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002421 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002422 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002423 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002424
ager@chromium.org7c537e22008-10-16 08:43:32 +00002425 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2426 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002427 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002428 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002429 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002430 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002431 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002432 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002433 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002434 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002435 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002436 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002437 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002438 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002439}
2440
2441
2442static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002443 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002444 ASSERT(args.length() == 3);
2445
ager@chromium.org7c537e22008-10-16 08:43:32 +00002446 CONVERT_ARG_CHECKED(String, sub, 0);
2447 CONVERT_ARG_CHECKED(String, pat, 1);
2448
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002449 Object* index = args[2];
2450 uint32_t start_index;
2451 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2452
ager@chromium.org870a0b62008-11-04 11:43:05 +00002453 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002454 int position = Runtime::StringMatch(sub, pat, start_index);
2455 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002456}
2457
2458
2459static Object* Runtime_StringLastIndexOf(Arguments args) {
2460 NoHandleAllocation ha;
2461 ASSERT(args.length() == 3);
2462
2463 CONVERT_CHECKED(String, sub, args[0]);
2464 CONVERT_CHECKED(String, pat, args[1]);
2465 Object* index = args[2];
2466
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002467 sub->TryFlattenIfNotFlat();
2468 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002469
2470 uint32_t start_index;
2471 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2472
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002473 uint32_t pattern_length = pat->length();
2474 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002475
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002476 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002477 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002478 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002479
2480 for (int i = start_index; i >= 0; i--) {
2481 bool found = true;
2482 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002483 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002484 found = false;
2485 break;
2486 }
2487 }
2488 if (found) return Smi::FromInt(i);
2489 }
2490
2491 return Smi::FromInt(-1);
2492}
2493
2494
2495static Object* Runtime_StringLocaleCompare(Arguments args) {
2496 NoHandleAllocation ha;
2497 ASSERT(args.length() == 2);
2498
2499 CONVERT_CHECKED(String, str1, args[0]);
2500 CONVERT_CHECKED(String, str2, args[1]);
2501
2502 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002503 int str1_length = str1->length();
2504 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002505
2506 // Decide trivial cases without flattening.
2507 if (str1_length == 0) {
2508 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2509 return Smi::FromInt(-str2_length);
2510 } else {
2511 if (str2_length == 0) return Smi::FromInt(str1_length);
2512 }
2513
2514 int end = str1_length < str2_length ? str1_length : str2_length;
2515
2516 // No need to flatten if we are going to find the answer on the first
2517 // character. At this point we know there is at least one character
2518 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002519 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002520 if (d != 0) return Smi::FromInt(d);
2521
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002522 str1->TryFlattenIfNotFlat();
2523 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524
2525 static StringInputBuffer buf1;
2526 static StringInputBuffer buf2;
2527
2528 buf1.Reset(str1);
2529 buf2.Reset(str2);
2530
2531 for (int i = 0; i < end; i++) {
2532 uint16_t char1 = buf1.GetNext();
2533 uint16_t char2 = buf2.GetNext();
2534 if (char1 != char2) return Smi::FromInt(char1 - char2);
2535 }
2536
2537 return Smi::FromInt(str1_length - str2_length);
2538}
2539
2540
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002541static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002542 NoHandleAllocation ha;
2543 ASSERT(args.length() == 3);
2544
2545 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002546 Object* from = args[1];
2547 Object* to = args[2];
2548 int start, end;
2549 // We have a fast integer-only case here to avoid a conversion to double in
2550 // the common case where from and to are Smis.
2551 if (from->IsSmi() && to->IsSmi()) {
2552 start = Smi::cast(from)->value();
2553 end = Smi::cast(to)->value();
2554 } else {
2555 CONVERT_DOUBLE_CHECKED(from_number, from);
2556 CONVERT_DOUBLE_CHECKED(to_number, to);
2557 start = FastD2I(from_number);
2558 end = FastD2I(to_number);
2559 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002560 RUNTIME_ASSERT(end >= start);
2561 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002562 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002563 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002564 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002565}
2566
2567
ager@chromium.org41826e72009-03-30 13:30:57 +00002568static Object* Runtime_StringMatch(Arguments args) {
2569 ASSERT_EQ(3, args.length());
2570
2571 CONVERT_ARG_CHECKED(String, subject, 0);
2572 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2573 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2574 HandleScope handles;
2575
2576 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2577
2578 if (match.is_null()) {
2579 return Failure::Exception();
2580 }
2581 if (match->IsNull()) {
2582 return Heap::null_value();
2583 }
2584 int length = subject->length();
2585
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002586 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002587 ZoneList<int> offsets(8);
2588 do {
2589 int start;
2590 int end;
2591 {
2592 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002593 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002594 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2595 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2596 }
2597 offsets.Add(start);
2598 offsets.Add(end);
2599 int index = start < end ? end : end + 1;
2600 if (index > length) break;
2601 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2602 if (match.is_null()) {
2603 return Failure::Exception();
2604 }
2605 } while (!match->IsNull());
2606 int matches = offsets.length() / 2;
2607 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2608 for (int i = 0; i < matches ; i++) {
2609 int from = offsets.at(i * 2);
2610 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002611 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002612 }
2613 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2614 result->set_length(Smi::FromInt(matches));
2615 return *result;
2616}
2617
2618
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002619static Object* Runtime_NumberToRadixString(Arguments args) {
2620 NoHandleAllocation ha;
2621 ASSERT(args.length() == 2);
2622
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002623 // Fast case where the result is a one character string.
2624 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2625 int value = Smi::cast(args[0])->value();
2626 int radix = Smi::cast(args[1])->value();
2627 if (value >= 0 && value < radix) {
2628 RUNTIME_ASSERT(radix <= 36);
2629 // Character array used for conversion.
2630 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2631 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2632 }
2633 }
2634
2635 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002636 CONVERT_DOUBLE_CHECKED(value, args[0]);
2637 if (isnan(value)) {
2638 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2639 }
2640 if (isinf(value)) {
2641 if (value < 0) {
2642 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2643 }
2644 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2645 }
2646 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2647 int radix = FastD2I(radix_number);
2648 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2649 char* str = DoubleToRadixCString(value, radix);
2650 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2651 DeleteArray(str);
2652 return result;
2653}
2654
2655
2656static Object* Runtime_NumberToFixed(Arguments args) {
2657 NoHandleAllocation ha;
2658 ASSERT(args.length() == 2);
2659
2660 CONVERT_DOUBLE_CHECKED(value, args[0]);
2661 if (isnan(value)) {
2662 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2663 }
2664 if (isinf(value)) {
2665 if (value < 0) {
2666 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2667 }
2668 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2669 }
2670 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2671 int f = FastD2I(f_number);
2672 RUNTIME_ASSERT(f >= 0);
2673 char* str = DoubleToFixedCString(value, f);
2674 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2675 DeleteArray(str);
2676 return res;
2677}
2678
2679
2680static Object* Runtime_NumberToExponential(Arguments args) {
2681 NoHandleAllocation ha;
2682 ASSERT(args.length() == 2);
2683
2684 CONVERT_DOUBLE_CHECKED(value, args[0]);
2685 if (isnan(value)) {
2686 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2687 }
2688 if (isinf(value)) {
2689 if (value < 0) {
2690 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2691 }
2692 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2693 }
2694 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2695 int f = FastD2I(f_number);
2696 RUNTIME_ASSERT(f >= -1 && f <= 20);
2697 char* str = DoubleToExponentialCString(value, f);
2698 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2699 DeleteArray(str);
2700 return res;
2701}
2702
2703
2704static Object* Runtime_NumberToPrecision(Arguments args) {
2705 NoHandleAllocation ha;
2706 ASSERT(args.length() == 2);
2707
2708 CONVERT_DOUBLE_CHECKED(value, args[0]);
2709 if (isnan(value)) {
2710 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2711 }
2712 if (isinf(value)) {
2713 if (value < 0) {
2714 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2715 }
2716 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2717 }
2718 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2719 int f = FastD2I(f_number);
2720 RUNTIME_ASSERT(f >= 1 && f <= 21);
2721 char* str = DoubleToPrecisionCString(value, f);
2722 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2723 DeleteArray(str);
2724 return res;
2725}
2726
2727
2728// Returns a single character string where first character equals
2729// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002730static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002731 if (index < static_cast<uint32_t>(string->length())) {
2732 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002733 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002734 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002735 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002736 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002737}
2738
2739
2740Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2741 // Handle [] indexing on Strings
2742 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002743 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2744 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002745 }
2746
2747 // Handle [] indexing on String objects
2748 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002749 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2750 Handle<Object> result =
2751 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2752 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002753 }
2754
2755 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002756 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002757 return prototype->GetElement(index);
2758 }
2759
2760 return object->GetElement(index);
2761}
2762
2763
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002764Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2765 HandleScope scope;
2766
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002767 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002768 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002769 Handle<Object> error =
2770 Factory::NewTypeError("non_object_property_load",
2771 HandleVector(args, 2));
2772 return Top::Throw(*error);
2773 }
2774
2775 // Check if the given key is an array index.
2776 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002777 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002778 return GetElementOrCharAt(object, index);
2779 }
2780
2781 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002782 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002783 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002784 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002785 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786 bool has_pending_exception = false;
2787 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002788 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002789 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002790 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002791 }
2792
ager@chromium.org32912102009-01-16 10:38:43 +00002793 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002794 // the element if so.
2795 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002796 return GetElementOrCharAt(object, index);
2797 } else {
2798 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002799 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002800 }
2801}
2802
2803
2804static Object* Runtime_GetProperty(Arguments args) {
2805 NoHandleAllocation ha;
2806 ASSERT(args.length() == 2);
2807
2808 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002809 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002810
2811 return Runtime::GetObjectProperty(object, key);
2812}
2813
2814
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002815// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002816static Object* Runtime_KeyedGetProperty(Arguments args) {
2817 NoHandleAllocation ha;
2818 ASSERT(args.length() == 2);
2819
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002820 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002821 // itself.
2822 //
2823 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002824 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002825 // global proxy object never has properties. This is the case
2826 // because the global proxy object forwards everything to its hidden
2827 // prototype including local lookups.
2828 //
2829 // Additionally, we need to make sure that we do not cache results
2830 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002831 if (args[0]->IsJSObject() &&
2832 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002833 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002834 args[1]->IsString()) {
2835 JSObject* receiver = JSObject::cast(args[0]);
2836 String* key = String::cast(args[1]);
2837 if (receiver->HasFastProperties()) {
2838 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002839 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002840 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2841 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002842 Object* value = receiver->FastPropertyAt(offset);
2843 return value->IsTheHole() ? Heap::undefined_value() : value;
2844 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002845 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002846 LookupResult result;
2847 receiver->LocalLookup(key, &result);
2848 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2849 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002850 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002851 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002852 }
2853 } else {
2854 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002855 StringDictionary* dictionary = receiver->property_dictionary();
2856 int entry = dictionary->FindEntry(key);
2857 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002858 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002859 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002860 if (!receiver->IsGlobalObject()) return value;
2861 value = JSGlobalPropertyCell::cast(value)->value();
2862 if (!value->IsTheHole()) return value;
2863 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002864 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002865 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002866 } else if (args[0]->IsString() && args[1]->IsSmi()) {
2867 // Fast case for string indexing using [] with a smi index.
2868 HandleScope scope;
2869 Handle<String> str = args.at<String>(0);
2870 int index = Smi::cast(args[1])->value();
2871 Handle<Object> result = GetCharAt(str, index);
2872 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002873 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002874
2875 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002876 return Runtime::GetObjectProperty(args.at<Object>(0),
2877 args.at<Object>(1));
2878}
2879
2880
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002881Object* Runtime::SetObjectProperty(Handle<Object> object,
2882 Handle<Object> key,
2883 Handle<Object> value,
2884 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002885 HandleScope scope;
2886
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002888 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002889 Handle<Object> error =
2890 Factory::NewTypeError("non_object_property_store",
2891 HandleVector(args, 2));
2892 return Top::Throw(*error);
2893 }
2894
2895 // If the object isn't a JavaScript object, we ignore the store.
2896 if (!object->IsJSObject()) return *value;
2897
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002898 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2899
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002900 // Check if the given key is an array index.
2901 uint32_t index;
2902 if (Array::IndexFromObject(*key, &index)) {
2903 ASSERT(attr == NONE);
2904
2905 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2906 // of a string using [] notation. We need to support this too in
2907 // JavaScript.
2908 // In the case of a String object we just need to redirect the assignment to
2909 // the underlying string if the index is in range. Since the underlying
2910 // string does nothing with the assignment then we can ignore such
2911 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002912 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002913 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002914 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002916 Handle<Object> result = SetElement(js_object, index, value);
2917 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002918 return *value;
2919 }
2920
2921 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002922 Handle<Object> result;
2923 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002924 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002925 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002926 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002927 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002928 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002929 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002930 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002931 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002932 return *value;
2933 }
2934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002935 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002936 bool has_pending_exception = false;
2937 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2938 if (has_pending_exception) return Failure::Exception();
2939 Handle<String> name = Handle<String>::cast(converted);
2940
2941 if (name->AsArrayIndex(&index)) {
2942 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002943 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002944 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002945 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002946 }
2947}
2948
2949
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002950Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2951 Handle<Object> key,
2952 Handle<Object> value,
2953 PropertyAttributes attr) {
2954 HandleScope scope;
2955
2956 // Check if the given key is an array index.
2957 uint32_t index;
2958 if (Array::IndexFromObject(*key, &index)) {
2959 ASSERT(attr == NONE);
2960
2961 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2962 // of a string using [] notation. We need to support this too in
2963 // JavaScript.
2964 // In the case of a String object we just need to redirect the assignment to
2965 // the underlying string if the index is in range. Since the underlying
2966 // string does nothing with the assignment then we can ignore such
2967 // assignments.
2968 if (js_object->IsStringObjectWithCharacterAt(index)) {
2969 return *value;
2970 }
2971
2972 return js_object->SetElement(index, *value);
2973 }
2974
2975 if (key->IsString()) {
2976 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2977 ASSERT(attr == NONE);
2978 return js_object->SetElement(index, *value);
2979 } else {
2980 Handle<String> key_string = Handle<String>::cast(key);
2981 key_string->TryFlattenIfNotFlat();
2982 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2983 *value,
2984 attr);
2985 }
2986 }
2987
2988 // Call-back into JavaScript to convert the key to a string.
2989 bool has_pending_exception = false;
2990 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2991 if (has_pending_exception) return Failure::Exception();
2992 Handle<String> name = Handle<String>::cast(converted);
2993
2994 if (name->AsArrayIndex(&index)) {
2995 ASSERT(attr == NONE);
2996 return js_object->SetElement(index, *value);
2997 } else {
2998 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2999 }
3000}
3001
3002
ager@chromium.orge2902be2009-06-08 12:21:35 +00003003Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3004 Handle<Object> key) {
3005 HandleScope scope;
3006
3007 // Check if the given key is an array index.
3008 uint32_t index;
3009 if (Array::IndexFromObject(*key, &index)) {
3010 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3011 // characters of a string using [] notation. In the case of a
3012 // String object we just need to redirect the deletion to the
3013 // underlying string if the index is in range. Since the
3014 // underlying string does nothing with the deletion, we can ignore
3015 // such deletions.
3016 if (js_object->IsStringObjectWithCharacterAt(index)) {
3017 return Heap::true_value();
3018 }
3019
3020 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3021 }
3022
3023 Handle<String> key_string;
3024 if (key->IsString()) {
3025 key_string = Handle<String>::cast(key);
3026 } else {
3027 // Call-back into JavaScript to convert the key to a string.
3028 bool has_pending_exception = false;
3029 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3030 if (has_pending_exception) return Failure::Exception();
3031 key_string = Handle<String>::cast(converted);
3032 }
3033
3034 key_string->TryFlattenIfNotFlat();
3035 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3036}
3037
3038
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003039static Object* Runtime_SetProperty(Arguments args) {
3040 NoHandleAllocation ha;
3041 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3042
3043 Handle<Object> object = args.at<Object>(0);
3044 Handle<Object> key = args.at<Object>(1);
3045 Handle<Object> value = args.at<Object>(2);
3046
3047 // Compute attributes.
3048 PropertyAttributes attributes = NONE;
3049 if (args.length() == 4) {
3050 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003051 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003052 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003053 RUNTIME_ASSERT(
3054 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3055 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003056 }
3057 return Runtime::SetObjectProperty(object, key, value, attributes);
3058}
3059
3060
3061// Set a local property, even if it is READ_ONLY. If the property does not
3062// exist, it will be added with attributes NONE.
3063static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
3064 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003065 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003066 CONVERT_CHECKED(JSObject, object, args[0]);
3067 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003068 // Compute attributes.
3069 PropertyAttributes attributes = NONE;
3070 if (args.length() == 4) {
3071 CONVERT_CHECKED(Smi, value_obj, args[3]);
3072 int unchecked_value = value_obj->value();
3073 // Only attribute bits should be set.
3074 RUNTIME_ASSERT(
3075 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3076 attributes = static_cast<PropertyAttributes>(unchecked_value);
3077 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003078
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003079 return object->
3080 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003081}
3082
3083
3084static Object* Runtime_DeleteProperty(Arguments args) {
3085 NoHandleAllocation ha;
3086 ASSERT(args.length() == 2);
3087
3088 CONVERT_CHECKED(JSObject, object, args[0]);
3089 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00003090 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003091}
3092
3093
ager@chromium.org9085a012009-05-11 19:22:57 +00003094static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
3095 Handle<String> key) {
3096 if (object->HasLocalProperty(*key)) return Heap::true_value();
3097 // Handle hidden prototypes. If there's a hidden prototype above this thing
3098 // then we have to check it for properties, because they are supposed to
3099 // look like they are on this object.
3100 Handle<Object> proto(object->GetPrototype());
3101 if (proto->IsJSObject() &&
3102 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
3103 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
3104 }
3105 return Heap::false_value();
3106}
3107
3108
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003109static Object* Runtime_HasLocalProperty(Arguments args) {
3110 NoHandleAllocation ha;
3111 ASSERT(args.length() == 2);
3112 CONVERT_CHECKED(String, key, args[1]);
3113
ager@chromium.org9085a012009-05-11 19:22:57 +00003114 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003115 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00003116 if (obj->IsJSObject()) {
3117 JSObject* object = JSObject::cast(obj);
3118 // Fast case - no interceptors.
3119 if (object->HasRealNamedProperty(key)) return Heap::true_value();
3120 // Slow case. Either it's not there or we have an interceptor. We should
3121 // have handles for this kind of deal.
3122 HandleScope scope;
3123 return HasLocalPropertyImplementation(Handle<JSObject>(object),
3124 Handle<String>(key));
3125 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003126 // Well, there is one exception: Handle [] on strings.
3127 uint32_t index;
3128 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00003129 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003130 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003131 return Heap::true_value();
3132 }
3133 }
3134 return Heap::false_value();
3135}
3136
3137
3138static Object* Runtime_HasProperty(Arguments args) {
3139 NoHandleAllocation na;
3140 ASSERT(args.length() == 2);
3141
3142 // Only JS objects can have properties.
3143 if (args[0]->IsJSObject()) {
3144 JSObject* object = JSObject::cast(args[0]);
3145 CONVERT_CHECKED(String, key, args[1]);
3146 if (object->HasProperty(key)) return Heap::true_value();
3147 }
3148 return Heap::false_value();
3149}
3150
3151
3152static Object* Runtime_HasElement(Arguments args) {
3153 NoHandleAllocation na;
3154 ASSERT(args.length() == 2);
3155
3156 // Only JS objects can have elements.
3157 if (args[0]->IsJSObject()) {
3158 JSObject* object = JSObject::cast(args[0]);
3159 CONVERT_CHECKED(Smi, index_obj, args[1]);
3160 uint32_t index = index_obj->value();
3161 if (object->HasElement(index)) return Heap::true_value();
3162 }
3163 return Heap::false_value();
3164}
3165
3166
3167static Object* Runtime_IsPropertyEnumerable(Arguments args) {
3168 NoHandleAllocation ha;
3169 ASSERT(args.length() == 2);
3170
3171 CONVERT_CHECKED(JSObject, object, args[0]);
3172 CONVERT_CHECKED(String, key, args[1]);
3173
3174 uint32_t index;
3175 if (key->AsArrayIndex(&index)) {
3176 return Heap::ToBoolean(object->HasElement(index));
3177 }
3178
ager@chromium.org870a0b62008-11-04 11:43:05 +00003179 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
3180 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003181}
3182
3183
3184static Object* Runtime_GetPropertyNames(Arguments args) {
3185 HandleScope scope;
3186 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003187 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003188 return *GetKeysFor(object);
3189}
3190
3191
3192// Returns either a FixedArray as Runtime_GetPropertyNames,
3193// or, if the given object has an enum cache that contains
3194// all enumerable properties of the object and its prototypes
3195// have none, the map of the object. This is used to speed up
3196// the check for deletions during a for-in.
3197static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3198 ASSERT(args.length() == 1);
3199
3200 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3201
3202 if (raw_object->IsSimpleEnum()) return raw_object->map();
3203
3204 HandleScope scope;
3205 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003206 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3207 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003208
3209 // Test again, since cache may have been built by preceding call.
3210 if (object->IsSimpleEnum()) return object->map();
3211
3212 return *content;
3213}
3214
3215
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003216static Object* Runtime_LocalKeys(Arguments args) {
3217 ASSERT_EQ(args.length(), 1);
3218 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3219 HandleScope scope;
3220 Handle<JSObject> object(raw_object);
3221 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3222 LOCAL_ONLY);
3223 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3224 // property array and since the result is mutable we have to create
3225 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003226 int length = contents->length();
3227 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3228 for (int i = 0; i < length; i++) {
3229 Object* entry = contents->get(i);
3230 if (entry->IsString()) {
3231 copy->set(i, entry);
3232 } else {
3233 ASSERT(entry->IsNumber());
3234 HandleScope scope;
3235 Handle<Object> entry_handle(entry);
3236 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3237 copy->set(i, *entry_str);
3238 }
3239 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003240 return *Factory::NewJSArrayWithElements(copy);
3241}
3242
3243
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003244static Object* Runtime_GetArgumentsProperty(Arguments args) {
3245 NoHandleAllocation ha;
3246 ASSERT(args.length() == 1);
3247
3248 // Compute the frame holding the arguments.
3249 JavaScriptFrameIterator it;
3250 it.AdvanceToArgumentsFrame();
3251 JavaScriptFrame* frame = it.frame();
3252
3253 // Get the actual number of provided arguments.
3254 const uint32_t n = frame->GetProvidedParametersCount();
3255
3256 // Try to convert the key to an index. If successful and within
3257 // index return the the argument from the frame.
3258 uint32_t index;
3259 if (Array::IndexFromObject(args[0], &index) && index < n) {
3260 return frame->GetParameter(index);
3261 }
3262
3263 // Convert the key to a string.
3264 HandleScope scope;
3265 bool exception = false;
3266 Handle<Object> converted =
3267 Execution::ToString(args.at<Object>(0), &exception);
3268 if (exception) return Failure::Exception();
3269 Handle<String> key = Handle<String>::cast(converted);
3270
3271 // Try to convert the string key into an array index.
3272 if (key->AsArrayIndex(&index)) {
3273 if (index < n) {
3274 return frame->GetParameter(index);
3275 } else {
3276 return Top::initial_object_prototype()->GetElement(index);
3277 }
3278 }
3279
3280 // Handle special arguments properties.
3281 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3282 if (key->Equals(Heap::callee_symbol())) return frame->function();
3283
3284 // Lookup in the initial Object.prototype object.
3285 return Top::initial_object_prototype()->GetProperty(*key);
3286}
3287
3288
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003289static Object* Runtime_ToFastProperties(Arguments args) {
3290 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003291 Handle<Object> object = args.at<Object>(0);
3292 if (object->IsJSObject()) {
3293 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3294 js_object->TransformToFastProperties(0);
3295 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003296 return *object;
3297}
3298
3299
3300static Object* Runtime_ToSlowProperties(Arguments args) {
3301 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003302 Handle<Object> object = args.at<Object>(0);
3303 if (object->IsJSObject()) {
3304 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003305 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003306 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003307 return *object;
3308}
3309
3310
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003311static Object* Runtime_ToBool(Arguments args) {
3312 NoHandleAllocation ha;
3313 ASSERT(args.length() == 1);
3314
3315 return args[0]->ToBoolean();
3316}
3317
3318
3319// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3320// Possible optimizations: put the type string into the oddballs.
3321static Object* Runtime_Typeof(Arguments args) {
3322 NoHandleAllocation ha;
3323
3324 Object* obj = args[0];
3325 if (obj->IsNumber()) return Heap::number_symbol();
3326 HeapObject* heap_obj = HeapObject::cast(obj);
3327
3328 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003329 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330
3331 InstanceType instance_type = heap_obj->map()->instance_type();
3332 if (instance_type < FIRST_NONSTRING_TYPE) {
3333 return Heap::string_symbol();
3334 }
3335
3336 switch (instance_type) {
3337 case ODDBALL_TYPE:
3338 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3339 return Heap::boolean_symbol();
3340 }
3341 if (heap_obj->IsNull()) {
3342 return Heap::object_symbol();
3343 }
3344 ASSERT(heap_obj->IsUndefined());
3345 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003346 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003347 return Heap::function_symbol();
3348 default:
3349 // For any kind of object not handled above, the spec rule for
3350 // host objects gives that it is okay to return "object"
3351 return Heap::object_symbol();
3352 }
3353}
3354
3355
3356static Object* Runtime_StringToNumber(Arguments args) {
3357 NoHandleAllocation ha;
3358 ASSERT(args.length() == 1);
3359 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003360 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3362}
3363
3364
3365static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3366 NoHandleAllocation ha;
3367 ASSERT(args.length() == 1);
3368
3369 CONVERT_CHECKED(JSArray, codes, args[0]);
3370 int length = Smi::cast(codes->length())->value();
3371
3372 // Check if the string can be ASCII.
3373 int i;
3374 for (i = 0; i < length; i++) {
3375 Object* element = codes->GetElement(i);
3376 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3377 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3378 break;
3379 }
3380
3381 Object* object = NULL;
3382 if (i == length) { // The string is ASCII.
3383 object = Heap::AllocateRawAsciiString(length);
3384 } else { // The string is not ASCII.
3385 object = Heap::AllocateRawTwoByteString(length);
3386 }
3387
3388 if (object->IsFailure()) return object;
3389 String* result = String::cast(object);
3390 for (int i = 0; i < length; i++) {
3391 Object* element = codes->GetElement(i);
3392 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003393 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003394 }
3395 return result;
3396}
3397
3398
3399// kNotEscaped is generated by the following:
3400//
3401// #!/bin/perl
3402// for (my $i = 0; $i < 256; $i++) {
3403// print "\n" if $i % 16 == 0;
3404// my $c = chr($i);
3405// my $escaped = 1;
3406// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3407// print $escaped ? "0, " : "1, ";
3408// }
3409
3410
3411static bool IsNotEscaped(uint16_t character) {
3412 // Only for 8 bit characters, the rest are always escaped (in a different way)
3413 ASSERT(character < 256);
3414 static const char kNotEscaped[256] = {
3415 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3416 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3417 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3418 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3419 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3420 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3421 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3422 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3423 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3425 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3426 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3427 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3428 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3429 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3430 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3431 };
3432 return kNotEscaped[character] != 0;
3433}
3434
3435
3436static Object* Runtime_URIEscape(Arguments args) {
3437 const char hex_chars[] = "0123456789ABCDEF";
3438 NoHandleAllocation ha;
3439 ASSERT(args.length() == 1);
3440 CONVERT_CHECKED(String, source, args[0]);
3441
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003442 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003443
3444 int escaped_length = 0;
3445 int length = source->length();
3446 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003447 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003448 buffer->Reset(source);
3449 while (buffer->has_more()) {
3450 uint16_t character = buffer->GetNext();
3451 if (character >= 256) {
3452 escaped_length += 6;
3453 } else if (IsNotEscaped(character)) {
3454 escaped_length++;
3455 } else {
3456 escaped_length += 3;
3457 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003458 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003459 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003460 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003461 Top::context()->mark_out_of_memory();
3462 return Failure::OutOfMemoryException();
3463 }
3464 }
3465 }
3466 // No length change implies no change. Return original string if no change.
3467 if (escaped_length == length) {
3468 return source;
3469 }
3470 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3471 if (o->IsFailure()) return o;
3472 String* destination = String::cast(o);
3473 int dest_position = 0;
3474
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003475 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003476 buffer->Rewind();
3477 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003478 uint16_t chr = buffer->GetNext();
3479 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003480 destination->Set(dest_position, '%');
3481 destination->Set(dest_position+1, 'u');
3482 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3483 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3484 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3485 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003486 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003487 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003488 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003489 dest_position++;
3490 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003491 destination->Set(dest_position, '%');
3492 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3493 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003494 dest_position += 3;
3495 }
3496 }
3497 return destination;
3498}
3499
3500
3501static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3502 static const signed char kHexValue['g'] = {
3503 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3504 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3505 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3506 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3507 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3508 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3509 -1, 10, 11, 12, 13, 14, 15 };
3510
3511 if (character1 > 'f') return -1;
3512 int hi = kHexValue[character1];
3513 if (hi == -1) return -1;
3514 if (character2 > 'f') return -1;
3515 int lo = kHexValue[character2];
3516 if (lo == -1) return -1;
3517 return (hi << 4) + lo;
3518}
3519
3520
ager@chromium.org870a0b62008-11-04 11:43:05 +00003521static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003522 int i,
3523 int length,
3524 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003525 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003526 int32_t hi = 0;
3527 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003528 if (character == '%' &&
3529 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003530 source->Get(i + 1) == 'u' &&
3531 (hi = TwoDigitHex(source->Get(i + 2),
3532 source->Get(i + 3))) != -1 &&
3533 (lo = TwoDigitHex(source->Get(i + 4),
3534 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003535 *step = 6;
3536 return (hi << 8) + lo;
3537 } else if (character == '%' &&
3538 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003539 (lo = TwoDigitHex(source->Get(i + 1),
3540 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003541 *step = 3;
3542 return lo;
3543 } else {
3544 *step = 1;
3545 return character;
3546 }
3547}
3548
3549
3550static Object* Runtime_URIUnescape(Arguments args) {
3551 NoHandleAllocation ha;
3552 ASSERT(args.length() == 1);
3553 CONVERT_CHECKED(String, source, args[0]);
3554
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003555 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003556
3557 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003558 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003559
3560 int unescaped_length = 0;
3561 for (int i = 0; i < length; unescaped_length++) {
3562 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003563 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003564 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003565 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003566 i += step;
3567 }
3568
3569 // No length change implies no change. Return original string if no change.
3570 if (unescaped_length == length)
3571 return source;
3572
3573 Object* o = ascii ?
3574 Heap::AllocateRawAsciiString(unescaped_length) :
3575 Heap::AllocateRawTwoByteString(unescaped_length);
3576 if (o->IsFailure()) return o;
3577 String* destination = String::cast(o);
3578
3579 int dest_position = 0;
3580 for (int i = 0; i < length; dest_position++) {
3581 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003582 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003583 i += step;
3584 }
3585 return destination;
3586}
3587
3588
3589static Object* Runtime_StringParseInt(Arguments args) {
3590 NoHandleAllocation ha;
3591
3592 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003593 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003594
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003595 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003596
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003597 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003598 int i;
3599
3600 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003601 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003602 if (i == len) return Heap::nan_value();
3603
3604 // Compute the sign (default to +).
3605 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003606 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607 sign = -1;
3608 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003609 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003610 i++;
3611 }
3612
3613 // Compute the radix if 0.
3614 if (radix == 0) {
3615 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003616 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003617 radix = 8;
3618 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003619 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003620 if (c == 'x' || c == 'X') {
3621 radix = 16;
3622 i += 2;
3623 }
3624 }
3625 }
3626 } else if (radix == 16) {
3627 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003628 if (i + 1 < len && s->Get(i) == '0') {
3629 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003630 if (c == 'x' || c == 'X') i += 2;
3631 }
3632 }
3633
3634 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3635 double value;
3636 int end_index = StringToInt(s, i, radix, &value);
3637 if (end_index != i) {
3638 return Heap::NumberFromDouble(sign * value);
3639 }
3640 return Heap::nan_value();
3641}
3642
3643
3644static Object* Runtime_StringParseFloat(Arguments args) {
3645 NoHandleAllocation ha;
3646 CONVERT_CHECKED(String, str, args[0]);
3647
3648 // ECMA-262 section 15.1.2.3, empty string is NaN
3649 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3650
3651 // Create a number object from the value.
3652 return Heap::NumberFromDouble(value);
3653}
3654
3655
3656static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3657static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3658
3659
3660template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003661static Object* ConvertCaseHelper(String* s,
3662 int length,
3663 int input_string_length,
3664 unibrow::Mapping<Converter, 128>* mapping) {
3665 // We try this twice, once with the assumption that the result is no longer
3666 // than the input and, if that assumption breaks, again with the exact
3667 // length. This may not be pretty, but it is nicer than what was here before
3668 // and I hereby claim my vaffel-is.
3669 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003670 // Allocate the resulting string.
3671 //
3672 // NOTE: This assumes that the upper/lower case of an ascii
3673 // character is also ascii. This is currently the case, but it
3674 // might break in the future if we implement more context and locale
3675 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003676 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003677 ? Heap::AllocateRawAsciiString(length)
3678 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003679 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003680 String* result = String::cast(o);
3681 bool has_changed_character = false;
3682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003683 // Convert all characters to upper case, assuming that they will fit
3684 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003685 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003687 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003688 // We can assume that the string is not empty
3689 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003690 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003691 bool has_next = buffer->has_more();
3692 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003693 int char_length = mapping->get(current, next, chars);
3694 if (char_length == 0) {
3695 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003696 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003697 i++;
3698 } else if (char_length == 1) {
3699 // Common case: converting the letter resulted in one character.
3700 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003701 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003702 has_changed_character = true;
3703 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003704 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003705 // We've assumed that the result would be as long as the
3706 // input but here is a character that converts to several
3707 // characters. No matter, we calculate the exact length
3708 // of the result and try the whole thing again.
3709 //
3710 // Note that this leaves room for optimization. We could just
3711 // memcpy what we already have to the result string. Also,
3712 // the result string is the last object allocated we could
3713 // "realloc" it and probably, in the vast majority of cases,
3714 // extend the existing string to be able to hold the full
3715 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003716 int next_length = 0;
3717 if (has_next) {
3718 next_length = mapping->get(next, 0, chars);
3719 if (next_length == 0) next_length = 1;
3720 }
3721 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003722 while (buffer->has_more()) {
3723 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003724 // NOTE: we use 0 as the next character here because, while
3725 // the next character may affect what a character converts to,
3726 // it does not in any case affect the length of what it convert
3727 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728 int char_length = mapping->get(current, 0, chars);
3729 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003730 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003731 if (current_length > Smi::kMaxValue) {
3732 Top::context()->mark_out_of_memory();
3733 return Failure::OutOfMemoryException();
3734 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003735 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003736 // Try again with the real length.
3737 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003738 } else {
3739 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003740 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003741 i++;
3742 }
3743 has_changed_character = true;
3744 }
3745 current = next;
3746 }
3747 if (has_changed_character) {
3748 return result;
3749 } else {
3750 // If we didn't actually change anything in doing the conversion
3751 // we simple return the result and let the converted string
3752 // become garbage; there is no reason to keep two identical strings
3753 // alive.
3754 return s;
3755 }
3756}
3757
3758
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003759template <class Converter>
3760static Object* ConvertCase(Arguments args,
3761 unibrow::Mapping<Converter, 128>* mapping) {
3762 NoHandleAllocation ha;
3763
3764 CONVERT_CHECKED(String, s, args[0]);
3765 s->TryFlattenIfNotFlat();
3766
3767 int input_string_length = s->length();
3768 // Assume that the string is not empty; we need this assumption later
3769 if (input_string_length == 0) return s;
3770 int length = input_string_length;
3771
3772 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3773 if (answer->IsSmi()) {
3774 // Retry with correct length.
3775 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3776 }
3777 return answer; // This may be a failure.
3778}
3779
3780
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003781static Object* Runtime_StringToLowerCase(Arguments args) {
3782 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3783}
3784
3785
3786static Object* Runtime_StringToUpperCase(Arguments args) {
3787 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3788}
3789
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003790static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
3791 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
3792}
3793
3794static Object* Runtime_StringTrim(Arguments args) {
3795 NoHandleAllocation ha;
3796 ASSERT(args.length() == 3);
3797
3798 CONVERT_CHECKED(String, s, args[0]);
3799 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
3800 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
3801
3802 s->TryFlattenIfNotFlat();
3803 int length = s->length();
3804
3805 int left = 0;
3806 if (trimLeft) {
3807 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
3808 left++;
3809 }
3810 }
3811
3812 int right = length;
3813 if (trimRight) {
3814 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
3815 right--;
3816 }
3817 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003818 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003819}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003820
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003821bool Runtime::IsUpperCaseChar(uint16_t ch) {
3822 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3823 int char_length = to_upper_mapping.get(ch, 0, chars);
3824 return char_length == 0;
3825}
3826
3827
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003828static Object* Runtime_NumberToString(Arguments args) {
3829 NoHandleAllocation ha;
3830 ASSERT(args.length() == 1);
3831
3832 Object* number = args[0];
3833 RUNTIME_ASSERT(number->IsNumber());
3834
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003835 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003836}
3837
3838
3839static Object* Runtime_NumberToInteger(Arguments args) {
3840 NoHandleAllocation ha;
3841 ASSERT(args.length() == 1);
3842
3843 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003844 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003845 CONVERT_DOUBLE_CHECKED(number, obj);
3846 return Heap::NumberFromDouble(DoubleToInteger(number));
3847}
3848
3849
3850static Object* Runtime_NumberToJSUint32(Arguments args) {
3851 NoHandleAllocation ha;
3852 ASSERT(args.length() == 1);
3853
3854 Object* obj = args[0];
3855 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3856 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3857 return Heap::NumberFromUint32(number);
3858}
3859
3860
3861static Object* Runtime_NumberToJSInt32(Arguments args) {
3862 NoHandleAllocation ha;
3863 ASSERT(args.length() == 1);
3864
3865 Object* obj = args[0];
3866 if (obj->IsSmi()) return obj;
3867 CONVERT_DOUBLE_CHECKED(number, obj);
3868 return Heap::NumberFromInt32(DoubleToInt32(number));
3869}
3870
3871
ager@chromium.org870a0b62008-11-04 11:43:05 +00003872// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3873// a small integer.
3874static Object* Runtime_NumberToSmi(Arguments args) {
3875 NoHandleAllocation ha;
3876 ASSERT(args.length() == 1);
3877
3878 Object* obj = args[0];
3879 if (obj->IsSmi()) {
3880 return obj;
3881 }
3882 if (obj->IsHeapNumber()) {
3883 double value = HeapNumber::cast(obj)->value();
3884 int int_value = FastD2I(value);
3885 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3886 return Smi::FromInt(int_value);
3887 }
3888 }
3889 return Heap::nan_value();
3890}
3891
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003892
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003893static Object* Runtime_NumberAdd(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 return Heap::AllocateHeapNumber(x + y);
3900}
3901
3902
3903static Object* Runtime_NumberSub(Arguments args) {
3904 NoHandleAllocation ha;
3905 ASSERT(args.length() == 2);
3906
3907 CONVERT_DOUBLE_CHECKED(x, args[0]);
3908 CONVERT_DOUBLE_CHECKED(y, args[1]);
3909 return Heap::AllocateHeapNumber(x - y);
3910}
3911
3912
3913static Object* Runtime_NumberMul(Arguments args) {
3914 NoHandleAllocation ha;
3915 ASSERT(args.length() == 2);
3916
3917 CONVERT_DOUBLE_CHECKED(x, args[0]);
3918 CONVERT_DOUBLE_CHECKED(y, args[1]);
3919 return Heap::AllocateHeapNumber(x * y);
3920}
3921
3922
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003923static Object* Runtime_NumberUnaryMinus(Arguments args) {
3924 NoHandleAllocation ha;
3925 ASSERT(args.length() == 1);
3926
3927 CONVERT_DOUBLE_CHECKED(x, args[0]);
3928 return Heap::AllocateHeapNumber(-x);
3929}
3930
3931
3932static Object* Runtime_NumberDiv(Arguments args) {
3933 NoHandleAllocation ha;
3934 ASSERT(args.length() == 2);
3935
3936 CONVERT_DOUBLE_CHECKED(x, args[0]);
3937 CONVERT_DOUBLE_CHECKED(y, args[1]);
3938 return Heap::NewNumberFromDouble(x / y);
3939}
3940
3941
3942static Object* Runtime_NumberMod(Arguments args) {
3943 NoHandleAllocation ha;
3944 ASSERT(args.length() == 2);
3945
3946 CONVERT_DOUBLE_CHECKED(x, args[0]);
3947 CONVERT_DOUBLE_CHECKED(y, args[1]);
3948
ager@chromium.org3811b432009-10-28 14:53:37 +00003949 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003950 // NewNumberFromDouble may return a Smi instead of a Number object
3951 return Heap::NewNumberFromDouble(x);
3952}
3953
3954
3955static Object* Runtime_StringAdd(Arguments args) {
3956 NoHandleAllocation ha;
3957 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003958 CONVERT_CHECKED(String, str1, args[0]);
3959 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003960 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003961 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003962}
3963
3964
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003965template<typename sinkchar>
3966static inline void StringBuilderConcatHelper(String* special,
3967 sinkchar* sink,
3968 FixedArray* fixed_array,
3969 int array_length) {
3970 int position = 0;
3971 for (int i = 0; i < array_length; i++) {
3972 Object* element = fixed_array->get(i);
3973 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003974 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003975 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003976 int pos;
3977 int len;
3978 if (encoded_slice > 0) {
3979 // Position and length encoded in one smi.
3980 pos = StringBuilderSubstringPosition::decode(encoded_slice);
3981 len = StringBuilderSubstringLength::decode(encoded_slice);
3982 } else {
3983 // Position and length encoded in two smis.
3984 Object* obj = fixed_array->get(++i);
3985 ASSERT(obj->IsSmi());
3986 pos = Smi::cast(obj)->value();
3987 len = -encoded_slice;
3988 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00003989 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003990 sink + position,
3991 pos,
3992 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003993 position += len;
3994 } else {
3995 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003996 int element_length = string->length();
3997 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003998 position += element_length;
3999 }
4000 }
4001}
4002
4003
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004004static Object* Runtime_StringBuilderConcat(Arguments args) {
4005 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004006 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004007 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004008 if (!args[1]->IsSmi()) {
4009 Top::context()->mark_out_of_memory();
4010 return Failure::OutOfMemoryException();
4011 }
4012 int array_length = Smi::cast(args[1])->value();
4013 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004014
4015 // This assumption is used by the slice encoding in one or two smis.
4016 ASSERT(Smi::kMaxValue >= String::kMaxLength);
4017
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004018 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004019 if (!array->HasFastElements()) {
4020 return Top::Throw(Heap::illegal_argument_symbol());
4021 }
4022 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004023 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004024 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004025 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004026
4027 if (array_length == 0) {
4028 return Heap::empty_string();
4029 } else if (array_length == 1) {
4030 Object* first = fixed_array->get(0);
4031 if (first->IsString()) return first;
4032 }
4033
ager@chromium.org5ec48922009-05-05 07:25:34 +00004034 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004035 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004036 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004037 for (int i = 0; i < array_length; i++) {
4038 Object* elt = fixed_array->get(i);
4039 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004040 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004041 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004042 if (len > 0) {
4043 // Position and length encoded in one smi.
4044 int pos = len >> 11;
4045 len &= 0x7ff;
4046 if (pos + len > special_length) {
4047 return Top::Throw(Heap::illegal_argument_symbol());
4048 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004049 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004050 } else {
4051 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004052 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004053 // Get the position and check that it is also a smi.
4054 i++;
4055 if (i >= array_length) {
4056 return Top::Throw(Heap::illegal_argument_symbol());
4057 }
4058 Object* pos = fixed_array->get(i);
4059 if (!pos->IsSmi()) {
4060 return Top::Throw(Heap::illegal_argument_symbol());
4061 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004062 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004063 } else if (elt->IsString()) {
4064 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004065 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004066 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004067 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004068 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004069 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004070 } else {
4071 return Top::Throw(Heap::illegal_argument_symbol());
4072 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004073 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004074 Top::context()->mark_out_of_memory();
4075 return Failure::OutOfMemoryException();
4076 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004077 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004078 }
4079
4080 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004081 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004082
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004083 if (ascii) {
4084 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004085 if (object->IsFailure()) return object;
4086 SeqAsciiString* answer = SeqAsciiString::cast(object);
4087 StringBuilderConcatHelper(special,
4088 answer->GetChars(),
4089 fixed_array,
4090 array_length);
4091 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004092 } else {
4093 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004094 if (object->IsFailure()) return object;
4095 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
4096 StringBuilderConcatHelper(special,
4097 answer->GetChars(),
4098 fixed_array,
4099 array_length);
4100 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004101 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004102}
4103
4104
4105static Object* Runtime_NumberOr(Arguments args) {
4106 NoHandleAllocation ha;
4107 ASSERT(args.length() == 2);
4108
4109 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4110 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4111 return Heap::NumberFromInt32(x | y);
4112}
4113
4114
4115static Object* Runtime_NumberAnd(Arguments args) {
4116 NoHandleAllocation ha;
4117 ASSERT(args.length() == 2);
4118
4119 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4120 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4121 return Heap::NumberFromInt32(x & y);
4122}
4123
4124
4125static Object* Runtime_NumberXor(Arguments args) {
4126 NoHandleAllocation ha;
4127 ASSERT(args.length() == 2);
4128
4129 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4130 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4131 return Heap::NumberFromInt32(x ^ y);
4132}
4133
4134
4135static Object* Runtime_NumberNot(Arguments args) {
4136 NoHandleAllocation ha;
4137 ASSERT(args.length() == 1);
4138
4139 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4140 return Heap::NumberFromInt32(~x);
4141}
4142
4143
4144static Object* Runtime_NumberShl(Arguments args) {
4145 NoHandleAllocation ha;
4146 ASSERT(args.length() == 2);
4147
4148 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4149 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4150 return Heap::NumberFromInt32(x << (y & 0x1f));
4151}
4152
4153
4154static Object* Runtime_NumberShr(Arguments args) {
4155 NoHandleAllocation ha;
4156 ASSERT(args.length() == 2);
4157
4158 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
4159 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4160 return Heap::NumberFromUint32(x >> (y & 0x1f));
4161}
4162
4163
4164static Object* Runtime_NumberSar(Arguments args) {
4165 NoHandleAllocation ha;
4166 ASSERT(args.length() == 2);
4167
4168 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4169 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4170 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
4171}
4172
4173
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004174static Object* Runtime_NumberEquals(Arguments args) {
4175 NoHandleAllocation ha;
4176 ASSERT(args.length() == 2);
4177
4178 CONVERT_DOUBLE_CHECKED(x, args[0]);
4179 CONVERT_DOUBLE_CHECKED(y, args[1]);
4180 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
4181 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
4182 if (x == y) return Smi::FromInt(EQUAL);
4183 Object* result;
4184 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
4185 result = Smi::FromInt(EQUAL);
4186 } else {
4187 result = Smi::FromInt(NOT_EQUAL);
4188 }
4189 return result;
4190}
4191
4192
4193static Object* Runtime_StringEquals(Arguments args) {
4194 NoHandleAllocation ha;
4195 ASSERT(args.length() == 2);
4196
4197 CONVERT_CHECKED(String, x, args[0]);
4198 CONVERT_CHECKED(String, y, args[1]);
4199
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004200 bool not_equal = !x->Equals(y);
4201 // This is slightly convoluted because the value that signifies
4202 // equality is 0 and inequality is 1 so we have to negate the result
4203 // from String::Equals.
4204 ASSERT(not_equal == 0 || not_equal == 1);
4205 STATIC_CHECK(EQUAL == 0);
4206 STATIC_CHECK(NOT_EQUAL == 1);
4207 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004208}
4209
4210
4211static Object* Runtime_NumberCompare(Arguments args) {
4212 NoHandleAllocation ha;
4213 ASSERT(args.length() == 3);
4214
4215 CONVERT_DOUBLE_CHECKED(x, args[0]);
4216 CONVERT_DOUBLE_CHECKED(y, args[1]);
4217 if (isnan(x) || isnan(y)) return args[2];
4218 if (x == y) return Smi::FromInt(EQUAL);
4219 if (isless(x, y)) return Smi::FromInt(LESS);
4220 return Smi::FromInt(GREATER);
4221}
4222
4223
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004224// Compare two Smis as if they were converted to strings and then
4225// compared lexicographically.
4226static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4227 NoHandleAllocation ha;
4228 ASSERT(args.length() == 2);
4229
4230 // Arrays for the individual characters of the two Smis. Smis are
4231 // 31 bit integers and 10 decimal digits are therefore enough.
4232 static int x_elms[10];
4233 static int y_elms[10];
4234
4235 // Extract the integer values from the Smis.
4236 CONVERT_CHECKED(Smi, x, args[0]);
4237 CONVERT_CHECKED(Smi, y, args[1]);
4238 int x_value = x->value();
4239 int y_value = y->value();
4240
4241 // If the integers are equal so are the string representations.
4242 if (x_value == y_value) return Smi::FromInt(EQUAL);
4243
4244 // If one of the integers are zero the normal integer order is the
4245 // same as the lexicographic order of the string representations.
4246 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4247
ager@chromium.org32912102009-01-16 10:38:43 +00004248 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004249 // smallest because the char code of '-' is less than the char code
4250 // of any digit. Otherwise, we make both values positive.
4251 if (x_value < 0 || y_value < 0) {
4252 if (y_value >= 0) return Smi::FromInt(LESS);
4253 if (x_value >= 0) return Smi::FromInt(GREATER);
4254 x_value = -x_value;
4255 y_value = -y_value;
4256 }
4257
4258 // Convert the integers to arrays of their decimal digits.
4259 int x_index = 0;
4260 int y_index = 0;
4261 while (x_value > 0) {
4262 x_elms[x_index++] = x_value % 10;
4263 x_value /= 10;
4264 }
4265 while (y_value > 0) {
4266 y_elms[y_index++] = y_value % 10;
4267 y_value /= 10;
4268 }
4269
4270 // Loop through the arrays of decimal digits finding the first place
4271 // where they differ.
4272 while (--x_index >= 0 && --y_index >= 0) {
4273 int diff = x_elms[x_index] - y_elms[y_index];
4274 if (diff != 0) return Smi::FromInt(diff);
4275 }
4276
4277 // If one array is a suffix of the other array, the longest array is
4278 // the representation of the largest of the Smis in the
4279 // lexicographic ordering.
4280 return Smi::FromInt(x_index - y_index);
4281}
4282
4283
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004284static Object* Runtime_StringCompare(Arguments args) {
4285 NoHandleAllocation ha;
4286 ASSERT(args.length() == 2);
4287
4288 CONVERT_CHECKED(String, x, args[0]);
4289 CONVERT_CHECKED(String, y, args[1]);
4290
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004291 Counters::string_compare_runtime.Increment();
4292
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004293 // A few fast case tests before we flatten.
4294 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004295 if (y->length() == 0) {
4296 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004297 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004298 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004299 return Smi::FromInt(LESS);
4300 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004301
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004302 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004303 if (d < 0) return Smi::FromInt(LESS);
4304 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004305
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004306 x->TryFlattenIfNotFlat();
4307 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004308
4309 static StringInputBuffer bufx;
4310 static StringInputBuffer bufy;
4311 bufx.Reset(x);
4312 bufy.Reset(y);
4313 while (bufx.has_more() && bufy.has_more()) {
4314 int d = bufx.GetNext() - bufy.GetNext();
4315 if (d < 0) return Smi::FromInt(LESS);
4316 else if (d > 0) return Smi::FromInt(GREATER);
4317 }
4318
4319 // x is (non-trivial) prefix of y:
4320 if (bufy.has_more()) return Smi::FromInt(LESS);
4321 // y is prefix of x:
4322 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4323}
4324
4325
4326static Object* Runtime_Math_abs(Arguments args) {
4327 NoHandleAllocation ha;
4328 ASSERT(args.length() == 1);
4329
4330 CONVERT_DOUBLE_CHECKED(x, args[0]);
4331 return Heap::AllocateHeapNumber(fabs(x));
4332}
4333
4334
4335static Object* Runtime_Math_acos(Arguments args) {
4336 NoHandleAllocation ha;
4337 ASSERT(args.length() == 1);
4338
4339 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004340 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004341}
4342
4343
4344static Object* Runtime_Math_asin(Arguments args) {
4345 NoHandleAllocation ha;
4346 ASSERT(args.length() == 1);
4347
4348 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004349 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004350}
4351
4352
4353static Object* Runtime_Math_atan(Arguments args) {
4354 NoHandleAllocation ha;
4355 ASSERT(args.length() == 1);
4356
4357 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004358 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004359}
4360
4361
4362static Object* Runtime_Math_atan2(Arguments args) {
4363 NoHandleAllocation ha;
4364 ASSERT(args.length() == 2);
4365
4366 CONVERT_DOUBLE_CHECKED(x, args[0]);
4367 CONVERT_DOUBLE_CHECKED(y, args[1]);
4368 double result;
4369 if (isinf(x) && isinf(y)) {
4370 // Make sure that the result in case of two infinite arguments
4371 // is a multiple of Pi / 4. The sign of the result is determined
4372 // by the first argument (x) and the sign of the second argument
4373 // determines the multiplier: one or three.
4374 static double kPiDividedBy4 = 0.78539816339744830962;
4375 int multiplier = (x < 0) ? -1 : 1;
4376 if (y < 0) multiplier *= 3;
4377 result = multiplier * kPiDividedBy4;
4378 } else {
4379 result = atan2(x, y);
4380 }
4381 return Heap::AllocateHeapNumber(result);
4382}
4383
4384
4385static Object* Runtime_Math_ceil(Arguments args) {
4386 NoHandleAllocation ha;
4387 ASSERT(args.length() == 1);
4388
4389 CONVERT_DOUBLE_CHECKED(x, args[0]);
4390 return Heap::NumberFromDouble(ceiling(x));
4391}
4392
4393
4394static Object* Runtime_Math_cos(Arguments args) {
4395 NoHandleAllocation ha;
4396 ASSERT(args.length() == 1);
4397
4398 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004399 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004400}
4401
4402
4403static Object* Runtime_Math_exp(Arguments args) {
4404 NoHandleAllocation ha;
4405 ASSERT(args.length() == 1);
4406
4407 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004408 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004409}
4410
4411
4412static Object* Runtime_Math_floor(Arguments args) {
4413 NoHandleAllocation ha;
4414 ASSERT(args.length() == 1);
4415
4416 CONVERT_DOUBLE_CHECKED(x, args[0]);
4417 return Heap::NumberFromDouble(floor(x));
4418}
4419
4420
4421static Object* Runtime_Math_log(Arguments args) {
4422 NoHandleAllocation ha;
4423 ASSERT(args.length() == 1);
4424
4425 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004426 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004427}
4428
4429
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004430// Helper function to compute x^y, where y is known to be an
4431// integer. Uses binary decomposition to limit the number of
4432// multiplications; see the discussion in "Hacker's Delight" by Henry
4433// S. Warren, Jr., figure 11-6, page 213.
4434static double powi(double x, int y) {
4435 ASSERT(y != kMinInt);
4436 unsigned n = (y < 0) ? -y : y;
4437 double m = x;
4438 double p = 1;
4439 while (true) {
4440 if ((n & 1) != 0) p *= m;
4441 n >>= 1;
4442 if (n == 0) {
4443 if (y < 0) {
4444 // Unfortunately, we have to be careful when p has reached
4445 // infinity in the computation, because sometimes the higher
4446 // internal precision in the pow() implementation would have
4447 // given us a finite p. This happens very rarely.
4448 double result = 1.0 / p;
4449 return (result == 0 && isinf(p))
4450 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4451 : result;
4452 } else {
4453 return p;
4454 }
4455 }
4456 m *= m;
4457 }
4458}
4459
4460
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004461static Object* Runtime_Math_pow(Arguments args) {
4462 NoHandleAllocation ha;
4463 ASSERT(args.length() == 2);
4464
4465 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004466
4467 // If the second argument is a smi, it is much faster to call the
4468 // custom powi() function than the generic pow().
4469 if (args[1]->IsSmi()) {
4470 int y = Smi::cast(args[1])->value();
4471 return Heap::AllocateHeapNumber(powi(x, y));
4472 }
4473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004474 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004475
4476 if (!isinf(x)) {
4477 if (y == 0.5) {
4478 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4479 // square root of a number. To speed up such computations, we
4480 // explictly check for this case and use the sqrt() function
4481 // which is faster than pow().
4482 return Heap::AllocateHeapNumber(sqrt(x));
4483 } else if (y == -0.5) {
4484 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4485 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4486 }
4487 }
4488
4489 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004490 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004491 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4492 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004493 } else {
4494 return Heap::AllocateHeapNumber(pow(x, y));
4495 }
4496}
4497
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004498
4499static Object* Runtime_Math_round(Arguments args) {
4500 NoHandleAllocation ha;
4501 ASSERT(args.length() == 1);
4502
4503 CONVERT_DOUBLE_CHECKED(x, args[0]);
4504 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4505 return Heap::NumberFromDouble(floor(x + 0.5));
4506}
4507
4508
4509static Object* Runtime_Math_sin(Arguments args) {
4510 NoHandleAllocation ha;
4511 ASSERT(args.length() == 1);
4512
4513 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004514 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004515}
4516
4517
4518static Object* Runtime_Math_sqrt(Arguments args) {
4519 NoHandleAllocation ha;
4520 ASSERT(args.length() == 1);
4521
4522 CONVERT_DOUBLE_CHECKED(x, args[0]);
4523 return Heap::AllocateHeapNumber(sqrt(x));
4524}
4525
4526
4527static Object* Runtime_Math_tan(Arguments args) {
4528 NoHandleAllocation ha;
4529 ASSERT(args.length() == 1);
4530
4531 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004532 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004533}
4534
4535
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004536// The NewArguments function is only used when constructing the
4537// arguments array when calling non-functions from JavaScript in
4538// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004539static Object* Runtime_NewArguments(Arguments args) {
4540 NoHandleAllocation ha;
4541 ASSERT(args.length() == 1);
4542
4543 // ECMA-262, 3rd., 10.1.8, p.39
4544 CONVERT_CHECKED(JSFunction, callee, args[0]);
4545
4546 // Compute the frame holding the arguments.
4547 JavaScriptFrameIterator it;
4548 it.AdvanceToArgumentsFrame();
4549 JavaScriptFrame* frame = it.frame();
4550
4551 const int length = frame->GetProvidedParametersCount();
4552 Object* result = Heap::AllocateArgumentsObject(callee, length);
4553 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004554 if (length > 0) {
4555 Object* obj = Heap::AllocateFixedArray(length);
4556 if (obj->IsFailure()) return obj;
4557 FixedArray* array = FixedArray::cast(obj);
4558 ASSERT(array->length() == length);
4559 WriteBarrierMode mode = array->GetWriteBarrierMode();
4560 for (int i = 0; i < length; i++) {
4561 array->set(i, frame->GetParameter(i), mode);
4562 }
4563 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004564 }
4565 return result;
4566}
4567
4568
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004569static Object* Runtime_NewArgumentsFast(Arguments args) {
4570 NoHandleAllocation ha;
4571 ASSERT(args.length() == 3);
4572
4573 JSFunction* callee = JSFunction::cast(args[0]);
4574 Object** parameters = reinterpret_cast<Object**>(args[1]);
4575 const int length = Smi::cast(args[2])->value();
4576
4577 Object* result = Heap::AllocateArgumentsObject(callee, length);
4578 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004579 // Allocate the elements if needed.
4580 if (length > 0) {
4581 // Allocate the fixed array.
4582 Object* obj = Heap::AllocateRawFixedArray(length);
4583 if (obj->IsFailure()) return obj;
4584 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4585 FixedArray* array = FixedArray::cast(obj);
4586 array->set_length(length);
4587 WriteBarrierMode mode = array->GetWriteBarrierMode();
4588 for (int i = 0; i < length; i++) {
4589 array->set(i, *--parameters, mode);
4590 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004591 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004592 }
4593 return result;
4594}
4595
4596
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004597static Object* Runtime_NewClosure(Arguments args) {
4598 HandleScope scope;
4599 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00004600 CONVERT_ARG_CHECKED(Context, context, 0);
4601 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004602
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004603 PretenureFlag pretenure = (context->global_context() == *context)
4604 ? TENURED // Allocate global closures in old space.
4605 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004606 Handle<JSFunction> result =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004607 Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004608 return *result;
4609}
4610
4611
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004612static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004613 // TODO(385): Change this to create a construct stub specialized for
4614 // the given map to make allocation of simple objects - and maybe
4615 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004616 if (FLAG_inline_new
4617 && shared->has_only_simple_this_property_assignments()) {
4618 ConstructStubCompiler compiler;
4619 Object* code = compiler.CompileConstructStub(*shared);
4620 if (code->IsFailure()) {
4621 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4622 }
4623 return Code::cast(code);
4624 }
4625
4626 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004627}
4628
4629
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004630static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004631 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004632 ASSERT(args.length() == 1);
4633
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004634 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004635
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004636 // If the constructor isn't a proper function we throw a type error.
4637 if (!constructor->IsJSFunction()) {
4638 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4639 Handle<Object> type_error =
4640 Factory::NewTypeError("not_constructor", arguments);
4641 return Top::Throw(*type_error);
4642 }
4643
4644 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004645#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004646 // Handle stepping into constructors if step into is active.
4647 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004648 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004649 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004650#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004651
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004652 if (function->has_initial_map()) {
4653 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004654 // The 'Function' function ignores the receiver object when
4655 // called using 'new' and creates a new JSFunction object that
4656 // is returned. The receiver object is only used for error
4657 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004658 // JSFunction. Factory::NewJSObject() should not be used to
4659 // allocate JSFunctions since it does not properly initialize
4660 // the shared part of the function. Since the receiver is
4661 // ignored anyway, we use the global object as the receiver
4662 // instead of a new JSFunction object. This way, errors are
4663 // reported the same way whether or not 'Function' is called
4664 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004665 return Top::context()->global();
4666 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004667 }
4668
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004669 // The function should be compiled for the optimization hints to be available.
4670 if (!function->shared()->is_compiled()) {
4671 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4672 CLEAR_EXCEPTION,
4673 0);
4674 }
4675
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004676 bool first_allocation = !function->has_initial_map();
4677 Handle<JSObject> result = Factory::NewJSObject(function);
4678 if (first_allocation) {
4679 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004680 Handle<Code> stub = Handle<Code>(
4681 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004682 function->shared()->set_construct_stub(*stub);
4683 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004684
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004685 Counters::constructed_objects.Increment();
4686 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004687
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004688 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004689}
4690
4691
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004692static Object* Runtime_LazyCompile(Arguments args) {
4693 HandleScope scope;
4694 ASSERT(args.length() == 1);
4695
4696 Handle<JSFunction> function = args.at<JSFunction>(0);
4697#ifdef DEBUG
4698 if (FLAG_trace_lazy) {
4699 PrintF("[lazy: ");
4700 function->shared()->name()->Print();
4701 PrintF("]\n");
4702 }
4703#endif
4704
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004705 // Compile the target function. Here we compile using CompileLazyInLoop in
4706 // order to get the optimized version. This helps code like delta-blue
4707 // that calls performance-critical routines through constructors. A
4708 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4709 // direct call. Since the in-loop tracking takes place through CallICs
4710 // this means that things called through constructors are never known to
4711 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004713 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004714 return Failure::Exception();
4715 }
4716
4717 return function->code();
4718}
4719
4720
4721static Object* Runtime_GetCalledFunction(Arguments args) {
4722 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004723 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004724 StackFrameIterator it;
4725 // Get past the JS-to-C exit frame.
4726 ASSERT(it.frame()->is_exit());
4727 it.Advance();
4728 // Get past the CALL_NON_FUNCTION activation frame.
4729 ASSERT(it.frame()->is_java_script());
4730 it.Advance();
4731 // Argument adaptor frames do not copy the function; we have to skip
4732 // past them to get to the real calling frame.
4733 if (it.frame()->is_arguments_adaptor()) it.Advance();
4734 // Get the function from the top of the expression stack of the
4735 // calling frame.
4736 StandardFrame* frame = StandardFrame::cast(it.frame());
4737 int index = frame->ComputeExpressionsCount() - 1;
4738 Object* result = frame->GetExpression(index);
4739 return result;
4740}
4741
4742
4743static Object* Runtime_GetFunctionDelegate(Arguments args) {
4744 HandleScope scope;
4745 ASSERT(args.length() == 1);
4746 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4747 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4748}
4749
4750
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004751static Object* Runtime_GetConstructorDelegate(Arguments args) {
4752 HandleScope scope;
4753 ASSERT(args.length() == 1);
4754 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4755 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4756}
4757
4758
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004759static Object* Runtime_NewContext(Arguments args) {
4760 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004761 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004762
kasper.lund7276f142008-07-30 08:49:36 +00004763 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004764 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4765 Object* result = Heap::AllocateFunctionContext(length, function);
4766 if (result->IsFailure()) return result;
4767
4768 Top::set_context(Context::cast(result));
4769
kasper.lund7276f142008-07-30 08:49:36 +00004770 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004771}
4772
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004773static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004774 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004775 Object* js_object = object;
4776 if (!js_object->IsJSObject()) {
4777 js_object = js_object->ToObject();
4778 if (js_object->IsFailure()) {
4779 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004780 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004781 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004782 Handle<Object> result =
4783 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4784 return Top::Throw(*result);
4785 }
4786 }
4787
4788 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004789 Heap::AllocateWithContext(Top::context(),
4790 JSObject::cast(js_object),
4791 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004792 if (result->IsFailure()) return result;
4793
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004794 Context* context = Context::cast(result);
4795 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004796
kasper.lund7276f142008-07-30 08:49:36 +00004797 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004798}
4799
4800
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004801static Object* Runtime_PushContext(Arguments args) {
4802 NoHandleAllocation ha;
4803 ASSERT(args.length() == 1);
4804 return PushContextHelper(args[0], false);
4805}
4806
4807
4808static Object* Runtime_PushCatchContext(Arguments args) {
4809 NoHandleAllocation ha;
4810 ASSERT(args.length() == 1);
4811 return PushContextHelper(args[0], true);
4812}
4813
4814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004815static Object* Runtime_LookupContext(Arguments args) {
4816 HandleScope scope;
4817 ASSERT(args.length() == 2);
4818
4819 CONVERT_ARG_CHECKED(Context, context, 0);
4820 CONVERT_ARG_CHECKED(String, name, 1);
4821
4822 int index;
4823 PropertyAttributes attributes;
4824 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004825 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004826 context->Lookup(name, flags, &index, &attributes);
4827
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004828 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004829 ASSERT(holder->IsJSObject());
4830 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004831 }
4832
4833 // No intermediate context found. Use global object by default.
4834 return Top::context()->global();
4835}
4836
4837
ager@chromium.orga1645e22009-09-09 19:27:10 +00004838// A mechanism to return a pair of Object pointers in registers (if possible).
4839// How this is achieved is calling convention-dependent.
4840// All currently supported x86 compiles uses calling conventions that are cdecl
4841// variants where a 64-bit value is returned in two 32-bit registers
4842// (edx:eax on ia32, r1:r0 on ARM).
4843// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4844// In Win64 calling convention, a struct of two pointers is returned in memory,
4845// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004846#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004847struct ObjectPair {
4848 Object* x;
4849 Object* y;
4850};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004851
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004852static inline ObjectPair MakePair(Object* x, Object* y) {
4853 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004854 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4855 // In Win64 they are assigned to a hidden first argument.
4856 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004857}
4858#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004859typedef uint64_t ObjectPair;
4860static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004861 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004862 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004863}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004864#endif
4865
4866
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004867static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004868 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4869 USE(attributes);
4870 return x->IsTheHole() ? Heap::undefined_value() : x;
4871}
4872
4873
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004874static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4875 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004876 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004877 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004878 JSFunction* context_extension_function =
4879 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004880 // If the holder isn't a context extension object, we just return it
4881 // as the receiver. This allows arguments objects to be used as
4882 // receivers, but only if they are put in the context scope chain
4883 // explicitly via a with-statement.
4884 Object* constructor = holder->map()->constructor();
4885 if (constructor != context_extension_function) return holder;
4886 // Fall back to using the global object as the receiver if the
4887 // property turns out to be a local variable allocated in a context
4888 // extension object - introduced via eval.
4889 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004890}
4891
4892
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004893static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004894 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004895 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004896
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004897 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004898 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004899 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004900 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004901 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004902
4903 int index;
4904 PropertyAttributes attributes;
4905 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004906 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004907 context->Lookup(name, flags, &index, &attributes);
4908
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004909 // If the index is non-negative, the slot has been found in a local
4910 // variable or a parameter. Read it from the context object or the
4911 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004912 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004913 // If the "property" we were looking for is a local variable or an
4914 // argument in a context, the receiver is the global object; see
4915 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4916 JSObject* receiver = Top::context()->global()->global_receiver();
4917 Object* value = (holder->IsContext())
4918 ? Context::cast(*holder)->get(index)
4919 : JSObject::cast(*holder)->GetElement(index);
4920 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004921 }
4922
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004923 // If the holder is found, we read the property from it.
4924 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004925 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004926 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004927 JSObject* receiver;
4928 if (object->IsGlobalObject()) {
4929 receiver = GlobalObject::cast(object)->global_receiver();
4930 } else if (context->is_exception_holder(*holder)) {
4931 receiver = Top::context()->global()->global_receiver();
4932 } else {
4933 receiver = ComputeReceiverForNonGlobal(object);
4934 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004935 // No need to unhole the value here. This is taken care of by the
4936 // GetProperty function.
4937 Object* value = object->GetProperty(*name);
4938 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004939 }
4940
4941 if (throw_error) {
4942 // The property doesn't exist - throw exception.
4943 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004944 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004945 return MakePair(Top::Throw(*reference_error), NULL);
4946 } else {
4947 // The property doesn't exist - return undefined
4948 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4949 }
4950}
4951
4952
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004953static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004954 return LoadContextSlotHelper(args, true);
4955}
4956
4957
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004958static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004959 return LoadContextSlotHelper(args, false);
4960}
4961
4962
4963static Object* Runtime_StoreContextSlot(Arguments args) {
4964 HandleScope scope;
4965 ASSERT(args.length() == 3);
4966
4967 Handle<Object> value(args[0]);
4968 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004969 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004970
4971 int index;
4972 PropertyAttributes attributes;
4973 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004974 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004975 context->Lookup(name, flags, &index, &attributes);
4976
4977 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004978 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004979 // Ignore if read_only variable.
4980 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004981 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004982 }
4983 } else {
4984 ASSERT((attributes & READ_ONLY) == 0);
4985 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004986 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004987 USE(result);
4988 ASSERT(!result->IsFailure());
4989 }
4990 return *value;
4991 }
4992
4993 // Slow case: The property is not in a FixedArray context.
4994 // It is either in an JSObject extension context or it was not found.
4995 Handle<JSObject> context_ext;
4996
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004997 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004998 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004999 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005000 } else {
5001 // The property was not found. It needs to be stored in the global context.
5002 ASSERT(attributes == ABSENT);
5003 attributes = NONE;
5004 context_ext = Handle<JSObject>(Top::context()->global());
5005 }
5006
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005007 // Set the property, but ignore if read_only variable on the context
5008 // extension object itself.
5009 if ((attributes & READ_ONLY) == 0 ||
5010 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005011 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
5012 if (set.is_null()) {
5013 // Failure::Exception is converted to a null handle in the
5014 // handle-based methods such as SetProperty. We therefore need
5015 // to convert null handles back to exceptions.
5016 ASSERT(Top::has_pending_exception());
5017 return Failure::Exception();
5018 }
5019 }
5020 return *value;
5021}
5022
5023
5024static Object* Runtime_Throw(Arguments args) {
5025 HandleScope scope;
5026 ASSERT(args.length() == 1);
5027
5028 return Top::Throw(args[0]);
5029}
5030
5031
5032static Object* Runtime_ReThrow(Arguments args) {
5033 HandleScope scope;
5034 ASSERT(args.length() == 1);
5035
5036 return Top::ReThrow(args[0]);
5037}
5038
5039
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005040static Object* Runtime_PromoteScheduledException(Arguments args) {
5041 ASSERT_EQ(0, args.length());
5042 return Top::PromoteScheduledException();
5043}
5044
5045
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005046static Object* Runtime_ThrowReferenceError(Arguments args) {
5047 HandleScope scope;
5048 ASSERT(args.length() == 1);
5049
5050 Handle<Object> name(args[0]);
5051 Handle<Object> reference_error =
5052 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5053 return Top::Throw(*reference_error);
5054}
5055
5056
5057static Object* Runtime_StackOverflow(Arguments args) {
5058 NoHandleAllocation na;
5059 return Top::StackOverflow();
5060}
5061
5062
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005063static Object* Runtime_StackGuard(Arguments args) {
5064 ASSERT(args.length() == 1);
5065
5066 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005067 if (StackGuard::IsStackOverflow()) {
5068 return Runtime_StackOverflow(args);
5069 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005070
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005071 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005072}
5073
5074
5075// NOTE: These PrintXXX functions are defined for all builds (not just
5076// DEBUG builds) because we may want to be able to trace function
5077// calls in all modes.
5078static void PrintString(String* str) {
5079 // not uncommon to have empty strings
5080 if (str->length() > 0) {
5081 SmartPointer<char> s =
5082 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
5083 PrintF("%s", *s);
5084 }
5085}
5086
5087
5088static void PrintObject(Object* obj) {
5089 if (obj->IsSmi()) {
5090 PrintF("%d", Smi::cast(obj)->value());
5091 } else if (obj->IsString() || obj->IsSymbol()) {
5092 PrintString(String::cast(obj));
5093 } else if (obj->IsNumber()) {
5094 PrintF("%g", obj->Number());
5095 } else if (obj->IsFailure()) {
5096 PrintF("<failure>");
5097 } else if (obj->IsUndefined()) {
5098 PrintF("<undefined>");
5099 } else if (obj->IsNull()) {
5100 PrintF("<null>");
5101 } else if (obj->IsTrue()) {
5102 PrintF("<true>");
5103 } else if (obj->IsFalse()) {
5104 PrintF("<false>");
5105 } else {
5106 PrintF("%p", obj);
5107 }
5108}
5109
5110
5111static int StackSize() {
5112 int n = 0;
5113 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
5114 return n;
5115}
5116
5117
5118static void PrintTransition(Object* result) {
5119 // indentation
5120 { const int nmax = 80;
5121 int n = StackSize();
5122 if (n <= nmax)
5123 PrintF("%4d:%*s", n, n, "");
5124 else
5125 PrintF("%4d:%*s", n, nmax, "...");
5126 }
5127
5128 if (result == NULL) {
5129 // constructor calls
5130 JavaScriptFrameIterator it;
5131 JavaScriptFrame* frame = it.frame();
5132 if (frame->IsConstructor()) PrintF("new ");
5133 // function name
5134 Object* fun = frame->function();
5135 if (fun->IsJSFunction()) {
5136 PrintObject(JSFunction::cast(fun)->shared()->name());
5137 } else {
5138 PrintObject(fun);
5139 }
5140 // function arguments
5141 // (we are intentionally only printing the actually
5142 // supplied parameters, not all parameters required)
5143 PrintF("(this=");
5144 PrintObject(frame->receiver());
5145 const int length = frame->GetProvidedParametersCount();
5146 for (int i = 0; i < length; i++) {
5147 PrintF(", ");
5148 PrintObject(frame->GetParameter(i));
5149 }
5150 PrintF(") {\n");
5151
5152 } else {
5153 // function result
5154 PrintF("} -> ");
5155 PrintObject(result);
5156 PrintF("\n");
5157 }
5158}
5159
5160
5161static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005162 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005163 NoHandleAllocation ha;
5164 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005165 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005166}
5167
5168
5169static Object* Runtime_TraceExit(Arguments args) {
5170 NoHandleAllocation ha;
5171 PrintTransition(args[0]);
5172 return args[0]; // return TOS
5173}
5174
5175
5176static Object* Runtime_DebugPrint(Arguments args) {
5177 NoHandleAllocation ha;
5178 ASSERT(args.length() == 1);
5179
5180#ifdef DEBUG
5181 if (args[0]->IsString()) {
5182 // If we have a string, assume it's a code "marker"
5183 // and print some interesting cpu debugging info.
5184 JavaScriptFrameIterator it;
5185 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005186 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
5187 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005188 } else {
5189 PrintF("DebugPrint: ");
5190 }
5191 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005192 if (args[0]->IsHeapObject()) {
5193 HeapObject::cast(args[0])->map()->Print();
5194 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005195#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005196 // ShortPrint is available in release mode. Print is not.
5197 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005198#endif
5199 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00005200 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005201
5202 return args[0]; // return TOS
5203}
5204
5205
5206static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005207 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005208 NoHandleAllocation ha;
5209 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005210 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005211}
5212
5213
mads.s.ager31e71382008-08-13 09:32:07 +00005214static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005215 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005216 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005217
5218 // According to ECMA-262, section 15.9.1, page 117, the precision of
5219 // the number in a Date object representing a particular instant in
5220 // time is milliseconds. Therefore, we floor the result of getting
5221 // the OS time.
5222 double millis = floor(OS::TimeCurrentMillis());
5223 return Heap::NumberFromDouble(millis);
5224}
5225
5226
5227static Object* Runtime_DateParseString(Arguments args) {
5228 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005229 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005230
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005231 CONVERT_ARG_CHECKED(String, str, 0);
5232 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005233
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005234 CONVERT_ARG_CHECKED(JSArray, output, 1);
5235 RUNTIME_ASSERT(output->HasFastElements());
5236
5237 AssertNoAllocation no_allocation;
5238
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005239 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005240 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5241 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005242 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005243 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005244 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005245 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005246 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5247 }
5248
5249 if (result) {
5250 return *output;
5251 } else {
5252 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005253 }
5254}
5255
5256
5257static Object* Runtime_DateLocalTimezone(Arguments args) {
5258 NoHandleAllocation ha;
5259 ASSERT(args.length() == 1);
5260
5261 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005262 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005263 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5264}
5265
5266
5267static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5268 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005269 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005270
5271 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5272}
5273
5274
5275static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5276 NoHandleAllocation ha;
5277 ASSERT(args.length() == 1);
5278
5279 CONVERT_DOUBLE_CHECKED(x, args[0]);
5280 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5281}
5282
5283
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005284static Object* Runtime_NumberIsFinite(Arguments args) {
5285 NoHandleAllocation ha;
5286 ASSERT(args.length() == 1);
5287
5288 CONVERT_DOUBLE_CHECKED(value, args[0]);
5289 Object* result;
5290 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5291 result = Heap::false_value();
5292 } else {
5293 result = Heap::true_value();
5294 }
5295 return result;
5296}
5297
5298
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005299static Object* Runtime_GlobalReceiver(Arguments args) {
5300 ASSERT(args.length() == 1);
5301 Object* global = args[0];
5302 if (!global->IsJSGlobalObject()) return Heap::null_value();
5303 return JSGlobalObject::cast(global)->global_receiver();
5304}
5305
5306
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005307static Object* Runtime_CompileString(Arguments args) {
5308 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005309 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005310 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005311 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005312
ager@chromium.org381abbb2009-02-25 13:23:22 +00005313 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005314 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005315 Compiler::ValidationState validate = (is_json->IsTrue())
5316 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005317 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5318 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005319 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005320 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005321 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005322 Handle<JSFunction> fun =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005323 Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005324 return *fun;
5325}
5326
5327
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005328static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
5329 ASSERT(args.length() == 3);
5330 if (!args[0]->IsJSFunction()) {
5331 return MakePair(Top::ThrowIllegalOperation(), NULL);
5332 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005333
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005334 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005335 Handle<JSFunction> callee = args.at<JSFunction>(0);
5336 Handle<Object> receiver; // Will be overwritten.
5337
5338 // Compute the calling context.
5339 Handle<Context> context = Handle<Context>(Top::context());
5340#ifdef DEBUG
5341 // Make sure Top::context() agrees with the old code that traversed
5342 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005343 StackFrameLocator locator;
5344 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005345 ASSERT(Context::cast(frame->context()) == *context);
5346#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005347
5348 // Find where the 'eval' symbol is bound. It is unaliased only if
5349 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005350 int index = -1;
5351 PropertyAttributes attributes = ABSENT;
5352 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005353 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5354 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005355 // Stop search when eval is found or when the global context is
5356 // reached.
5357 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005358 if (context->is_function_context()) {
5359 context = Handle<Context>(Context::cast(context->closure()->context()));
5360 } else {
5361 context = Handle<Context>(context->previous());
5362 }
5363 }
5364
iposva@chromium.org245aa852009-02-10 00:49:54 +00005365 // If eval could not be resolved, it has been deleted and we need to
5366 // throw a reference error.
5367 if (attributes == ABSENT) {
5368 Handle<Object> name = Factory::eval_symbol();
5369 Handle<Object> reference_error =
5370 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005371 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005372 }
5373
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005374 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005375 // 'eval' is not bound in the global context. Just call the function
5376 // with the given arguments. This is not necessarily the global eval.
5377 if (receiver->IsContext()) {
5378 context = Handle<Context>::cast(receiver);
5379 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005380 } else if (receiver->IsJSContextExtensionObject()) {
5381 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005382 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005383 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005384 }
5385
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005386 // 'eval' is bound in the global context, but it may have been overwritten.
5387 // Compare it to the builtin 'GlobalEval' function to make sure.
5388 if (*callee != Top::global_context()->global_eval_fun() ||
5389 !args[1]->IsString()) {
5390 return MakePair(*callee, Top::context()->global()->global_receiver());
5391 }
5392
5393 // Deal with a normal eval call with a string argument. Compile it
5394 // and return the compiled function bound in the local context.
5395 Handle<String> source = args.at<String>(1);
5396 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5397 source,
5398 Handle<Context>(Top::context()),
5399 Top::context()->IsGlobalContext(),
5400 Compiler::DONT_VALIDATE_JSON);
5401 if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
5402 callee = Factory::NewFunctionFromBoilerplate(
5403 boilerplate,
5404 Handle<Context>(Top::context()),
5405 NOT_TENURED);
5406 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005407}
5408
5409
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005410static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5411 // This utility adjusts the property attributes for newly created Function
5412 // object ("new Function(...)") by changing the map.
5413 // All it does is changing the prototype property to enumerable
5414 // as specified in ECMA262, 15.3.5.2.
5415 HandleScope scope;
5416 ASSERT(args.length() == 1);
5417 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5418 ASSERT(func->map()->instance_type() ==
5419 Top::function_instance_map()->instance_type());
5420 ASSERT(func->map()->instance_size() ==
5421 Top::function_instance_map()->instance_size());
5422 func->set_map(*Top::function_instance_map());
5423 return *func;
5424}
5425
5426
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005427// Push an array unto an array of arrays if it is not already in the
5428// array. Returns true if the element was pushed on the stack and
5429// false otherwise.
5430static Object* Runtime_PushIfAbsent(Arguments args) {
5431 ASSERT(args.length() == 2);
5432 CONVERT_CHECKED(JSArray, array, args[0]);
5433 CONVERT_CHECKED(JSArray, element, args[1]);
5434 RUNTIME_ASSERT(array->HasFastElements());
5435 int length = Smi::cast(array->length())->value();
5436 FixedArray* elements = FixedArray::cast(array->elements());
5437 for (int i = 0; i < length; i++) {
5438 if (elements->get(i) == element) return Heap::false_value();
5439 }
5440 Object* obj = array->SetFastElement(length, element);
5441 if (obj->IsFailure()) return obj;
5442 return Heap::true_value();
5443}
5444
5445
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005446/**
5447 * A simple visitor visits every element of Array's.
5448 * The backend storage can be a fixed array for fast elements case,
5449 * or a dictionary for sparse array. Since Dictionary is a subtype
5450 * of FixedArray, the class can be used by both fast and slow cases.
5451 * The second parameter of the constructor, fast_elements, specifies
5452 * whether the storage is a FixedArray or Dictionary.
5453 *
5454 * An index limit is used to deal with the situation that a result array
5455 * length overflows 32-bit non-negative integer.
5456 */
5457class ArrayConcatVisitor {
5458 public:
5459 ArrayConcatVisitor(Handle<FixedArray> storage,
5460 uint32_t index_limit,
5461 bool fast_elements) :
5462 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005463 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005464
5465 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005466 if (i >= index_limit_ - index_offset_) return;
5467 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005468
5469 if (fast_elements_) {
5470 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5471 storage_->set(index, *elm);
5472
5473 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005474 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5475 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005476 Factory::DictionaryAtNumberPut(dict, index, elm);
5477 if (!result.is_identical_to(dict))
5478 storage_ = result;
5479 }
5480 }
5481
5482 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005483 if (index_limit_ - index_offset_ < delta) {
5484 index_offset_ = index_limit_;
5485 } else {
5486 index_offset_ += delta;
5487 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005488 }
5489
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00005490 Handle<FixedArray> storage() { return storage_; }
5491
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005492 private:
5493 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005494 // Limit on the accepted indices. Elements with indices larger than the
5495 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005496 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005497 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005498 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005499 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005500};
5501
5502
ager@chromium.org3811b432009-10-28 14:53:37 +00005503template<class ExternalArrayClass, class ElementType>
5504static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5505 bool elements_are_ints,
5506 bool elements_are_guaranteed_smis,
5507 uint32_t range,
5508 ArrayConcatVisitor* visitor) {
5509 Handle<ExternalArrayClass> array(
5510 ExternalArrayClass::cast(receiver->elements()));
5511 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5512
5513 if (visitor != NULL) {
5514 if (elements_are_ints) {
5515 if (elements_are_guaranteed_smis) {
5516 for (uint32_t j = 0; j < len; j++) {
5517 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5518 visitor->visit(j, e);
5519 }
5520 } else {
5521 for (uint32_t j = 0; j < len; j++) {
5522 int64_t val = static_cast<int64_t>(array->get(j));
5523 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5524 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5525 visitor->visit(j, e);
5526 } else {
5527 Handle<Object> e(
5528 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5529 visitor->visit(j, e);
5530 }
5531 }
5532 }
5533 } else {
5534 for (uint32_t j = 0; j < len; j++) {
5535 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5536 visitor->visit(j, e);
5537 }
5538 }
5539 }
5540
5541 return len;
5542}
5543
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005544/**
5545 * A helper function that visits elements of a JSObject. Only elements
5546 * whose index between 0 and range (exclusive) are visited.
5547 *
5548 * If the third parameter, visitor, is not NULL, the visitor is called
5549 * with parameters, 'visitor_index_offset + element index' and the element.
5550 *
5551 * It returns the number of visisted elements.
5552 */
5553static uint32_t IterateElements(Handle<JSObject> receiver,
5554 uint32_t range,
5555 ArrayConcatVisitor* visitor) {
5556 uint32_t num_of_elements = 0;
5557
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005558 switch (receiver->GetElementsKind()) {
5559 case JSObject::FAST_ELEMENTS: {
5560 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5561 uint32_t len = elements->length();
5562 if (range < len) {
5563 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005564 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005565
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005566 for (uint32_t j = 0; j < len; j++) {
5567 Handle<Object> e(elements->get(j));
5568 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005569 num_of_elements++;
5570 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005571 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005572 }
5573 }
5574 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005575 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005576 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005577 case JSObject::PIXEL_ELEMENTS: {
5578 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5579 uint32_t len = pixels->length();
5580 if (range < len) {
5581 len = range;
5582 }
5583
5584 for (uint32_t j = 0; j < len; j++) {
5585 num_of_elements++;
5586 if (visitor != NULL) {
5587 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5588 visitor->visit(j, e);
5589 }
5590 }
5591 break;
5592 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005593 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5594 num_of_elements =
5595 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5596 receiver, true, true, range, visitor);
5597 break;
5598 }
5599 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5600 num_of_elements =
5601 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5602 receiver, true, true, range, visitor);
5603 break;
5604 }
5605 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5606 num_of_elements =
5607 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5608 receiver, true, true, range, visitor);
5609 break;
5610 }
5611 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5612 num_of_elements =
5613 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5614 receiver, true, true, range, visitor);
5615 break;
5616 }
5617 case JSObject::EXTERNAL_INT_ELEMENTS: {
5618 num_of_elements =
5619 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5620 receiver, true, false, range, visitor);
5621 break;
5622 }
5623 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5624 num_of_elements =
5625 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5626 receiver, true, false, range, visitor);
5627 break;
5628 }
5629 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5630 num_of_elements =
5631 IterateExternalArrayElements<ExternalFloatArray, float>(
5632 receiver, false, false, range, visitor);
5633 break;
5634 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005635 case JSObject::DICTIONARY_ELEMENTS: {
5636 Handle<NumberDictionary> dict(receiver->element_dictionary());
5637 uint32_t capacity = dict->Capacity();
5638 for (uint32_t j = 0; j < capacity; j++) {
5639 Handle<Object> k(dict->KeyAt(j));
5640 if (dict->IsKey(*k)) {
5641 ASSERT(k->IsNumber());
5642 uint32_t index = static_cast<uint32_t>(k->Number());
5643 if (index < range) {
5644 num_of_elements++;
5645 if (visitor) {
5646 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5647 }
5648 }
5649 }
5650 }
5651 break;
5652 }
5653 default:
5654 UNREACHABLE();
5655 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005656 }
5657
5658 return num_of_elements;
5659}
5660
5661
5662/**
5663 * A helper function that visits elements of an Array object, and elements
5664 * on its prototypes.
5665 *
5666 * Elements on prototypes are visited first, and only elements whose indices
5667 * less than Array length are visited.
5668 *
5669 * If a ArrayConcatVisitor object is given, the visitor is called with
5670 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005671 *
5672 * The returned number of elements is an upper bound on the actual number
5673 * of elements added. If the same element occurs in more than one object
5674 * in the array's prototype chain, it will be counted more than once, but
5675 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005676 */
5677static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5678 ArrayConcatVisitor* visitor) {
5679 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5680 Handle<Object> obj = array;
5681
5682 static const int kEstimatedPrototypes = 3;
5683 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5684
5685 // Visit prototype first. If an element on the prototype is shadowed by
5686 // the inheritor using the same index, the ArrayConcatVisitor visits
5687 // the prototype element before the shadowing element.
5688 // The visitor can simply overwrite the old value by new value using
5689 // the same index. This follows Array::concat semantics.
5690 while (!obj->IsNull()) {
5691 objects.Add(Handle<JSObject>::cast(obj));
5692 obj = Handle<Object>(obj->GetPrototype());
5693 }
5694
5695 uint32_t nof_elements = 0;
5696 for (int i = objects.length() - 1; i >= 0; i--) {
5697 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005698 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005699 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005700
5701 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
5702 nof_elements = JSObject::kMaxElementCount;
5703 } else {
5704 nof_elements += encountered_elements;
5705 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005706 }
5707
5708 return nof_elements;
5709}
5710
5711
5712/**
5713 * A helper function of Runtime_ArrayConcat.
5714 *
5715 * The first argument is an Array of arrays and objects. It is the
5716 * same as the arguments array of Array::concat JS function.
5717 *
5718 * If an argument is an Array object, the function visits array
5719 * elements. If an argument is not an Array object, the function
5720 * visits the object as if it is an one-element array.
5721 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005722 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005723 * non-negative number is used as new length. For example, if one
5724 * array length is 2^32 - 1, second array length is 1, the
5725 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005726 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
5727 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005728 */
5729static uint32_t IterateArguments(Handle<JSArray> arguments,
5730 ArrayConcatVisitor* visitor) {
5731 uint32_t visited_elements = 0;
5732 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5733
5734 for (uint32_t i = 0; i < num_of_args; i++) {
5735 Handle<Object> obj(arguments->GetElement(i));
5736 if (obj->IsJSArray()) {
5737 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5738 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5739 uint32_t nof_elements =
5740 IterateArrayAndPrototypeElements(array, visitor);
5741 // Total elements of array and its prototype chain can be more than
5742 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005743 // the array length number of elements. We use the length as an estimate
5744 // for the actual number of elements added.
5745 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
5746 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
5747 visited_elements = JSArray::kMaxElementCount;
5748 } else {
5749 visited_elements += added_elements;
5750 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005751 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005752 } else {
5753 if (visitor) {
5754 visitor->visit(0, obj);
5755 visitor->increase_index_offset(1);
5756 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005757 if (visited_elements < JSArray::kMaxElementCount) {
5758 visited_elements++;
5759 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005760 }
5761 }
5762 return visited_elements;
5763}
5764
5765
5766/**
5767 * Array::concat implementation.
5768 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005769 * TODO(lrn): Fix non-compliance for very large concatenations and update to
5770 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005771 */
5772static Object* Runtime_ArrayConcat(Arguments args) {
5773 ASSERT(args.length() == 1);
5774 HandleScope handle_scope;
5775
5776 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5777 Handle<JSArray> arguments(arg_arrays);
5778
5779 // Pass 1: estimate the number of elements of the result
5780 // (it could be more than real numbers if prototype has elements).
5781 uint32_t result_length = 0;
5782 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5783
5784 { AssertNoAllocation nogc;
5785 for (uint32_t i = 0; i < num_of_args; i++) {
5786 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005787 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005788 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005789 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005790 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5791 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005792 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005793 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005794 if (JSObject::kMaxElementCount - result_length < length_estimate) {
5795 result_length = JSObject::kMaxElementCount;
5796 break;
5797 }
5798 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005799 }
5800 }
5801
5802 // Allocate an empty array, will set length and content later.
5803 Handle<JSArray> result = Factory::NewJSArray(0);
5804
5805 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5806 // If estimated number of elements is more than half of length, a
5807 // fixed array (fast case) is more time and space-efficient than a
5808 // dictionary.
5809 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5810
5811 Handle<FixedArray> storage;
5812 if (fast_case) {
5813 // The backing storage array must have non-existing elements to
5814 // preserve holes across concat operations.
5815 storage = Factory::NewFixedArrayWithHoles(result_length);
5816
5817 } else {
5818 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5819 uint32_t at_least_space_for = estimate_nof_elements +
5820 (estimate_nof_elements >> 2);
5821 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005822 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005823 }
5824
5825 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5826
5827 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5828
5829 IterateArguments(arguments, &visitor);
5830
5831 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00005832 // Please note the storage might have changed in the visitor.
5833 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005834
5835 return *result;
5836}
5837
5838
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005839// This will not allocate (flatten the string), but it may run
5840// very slowly for very deeply nested ConsStrings. For debugging use only.
5841static Object* Runtime_GlobalPrint(Arguments args) {
5842 NoHandleAllocation ha;
5843 ASSERT(args.length() == 1);
5844
5845 CONVERT_CHECKED(String, string, args[0]);
5846 StringInputBuffer buffer(string);
5847 while (buffer.has_more()) {
5848 uint16_t character = buffer.GetNext();
5849 PrintF("%c", character);
5850 }
5851 return string;
5852}
5853
ager@chromium.org5ec48922009-05-05 07:25:34 +00005854// Moves all own elements of an object, that are below a limit, to positions
5855// starting at zero. All undefined values are placed after non-undefined values,
5856// and are followed by non-existing element. Does not change the length
5857// property.
5858// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005859static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005860 ASSERT(args.length() == 2);
5861 CONVERT_CHECKED(JSObject, object, args[0]);
5862 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5863 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005864}
5865
5866
5867// Move contents of argument 0 (an array) to argument 1 (an array)
5868static Object* Runtime_MoveArrayContents(Arguments args) {
5869 ASSERT(args.length() == 2);
5870 CONVERT_CHECKED(JSArray, from, args[0]);
5871 CONVERT_CHECKED(JSArray, to, args[1]);
5872 to->SetContent(FixedArray::cast(from->elements()));
5873 to->set_length(from->length());
5874 from->SetContent(Heap::empty_fixed_array());
5875 from->set_length(0);
5876 return to;
5877}
5878
5879
5880// How many elements does this array have?
5881static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5882 ASSERT(args.length() == 1);
5883 CONVERT_CHECKED(JSArray, array, args[0]);
5884 HeapObject* elements = array->elements();
5885 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005886 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887 } else {
5888 return array->length();
5889 }
5890}
5891
5892
5893// Returns an array that tells you where in the [0, length) interval an array
5894// might have elements. Can either return keys or intervals. Keys can have
5895// gaps in (undefined). Intervals can also span over some undefined keys.
5896static Object* Runtime_GetArrayKeys(Arguments args) {
5897 ASSERT(args.length() == 2);
5898 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005899 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005900 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005901 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005902 // Create an array and get all the keys into it, then remove all the
5903 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005904 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905 int keys_length = keys->length();
5906 for (int i = 0; i < keys_length; i++) {
5907 Object* key = keys->get(i);
5908 uint32_t index;
5909 if (!Array::IndexFromObject(key, &index) || index >= length) {
5910 // Zap invalid keys.
5911 keys->set_undefined(i);
5912 }
5913 }
5914 return *Factory::NewJSArrayWithElements(keys);
5915 } else {
5916 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5917 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005918 single_interval->set(0,
5919 Smi::FromInt(-1),
5920 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005921 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5922 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005923 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005924 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005925 single_interval->set(1, *length_object);
5926 return *Factory::NewJSArrayWithElements(single_interval);
5927 }
5928}
5929
5930
5931// DefineAccessor takes an optional final argument which is the
5932// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5933// to the way accessors are implemented, it is set for both the getter
5934// and setter on the first call to DefineAccessor and ignored on
5935// subsequent calls.
5936static Object* Runtime_DefineAccessor(Arguments args) {
5937 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5938 // Compute attributes.
5939 PropertyAttributes attributes = NONE;
5940 if (args.length() == 5) {
5941 CONVERT_CHECKED(Smi, attrs, args[4]);
5942 int value = attrs->value();
5943 // Only attribute bits should be set.
5944 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5945 attributes = static_cast<PropertyAttributes>(value);
5946 }
5947
5948 CONVERT_CHECKED(JSObject, obj, args[0]);
5949 CONVERT_CHECKED(String, name, args[1]);
5950 CONVERT_CHECKED(Smi, flag, args[2]);
5951 CONVERT_CHECKED(JSFunction, fun, args[3]);
5952 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5953}
5954
5955
5956static Object* Runtime_LookupAccessor(Arguments args) {
5957 ASSERT(args.length() == 3);
5958 CONVERT_CHECKED(JSObject, obj, args[0]);
5959 CONVERT_CHECKED(String, name, args[1]);
5960 CONVERT_CHECKED(Smi, flag, args[2]);
5961 return obj->LookupAccessor(name, flag->value() == 0);
5962}
5963
5964
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005965#ifdef ENABLE_DEBUGGER_SUPPORT
5966static Object* Runtime_DebugBreak(Arguments args) {
5967 ASSERT(args.length() == 0);
5968 return Execution::DebugBreakHelper();
5969}
5970
5971
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005972// Helper functions for wrapping and unwrapping stack frame ids.
5973static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005974 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005975 return Smi::FromInt(id >> 2);
5976}
5977
5978
5979static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5980 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5981}
5982
5983
5984// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005985// args[0]: debug event listener function to set or null or undefined for
5986// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005987// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005988static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005989 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005990 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5991 args[0]->IsUndefined() ||
5992 args[0]->IsNull());
5993 Handle<Object> callback = args.at<Object>(0);
5994 Handle<Object> data = args.at<Object>(1);
5995 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005996
5997 return Heap::undefined_value();
5998}
5999
6000
6001static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006002 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006003 StackGuard::DebugBreak();
6004 return Heap::undefined_value();
6005}
6006
6007
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006008// Find the length of the prototype chain that is to to handled as one. If a
6009// prototype object is hidden it is to be viewed as part of the the object it
6010// is prototype for.
6011static int LocalPrototypeChainLength(JSObject* obj) {
6012 int count = 1;
6013 Object* proto = obj->GetPrototype();
6014 while (proto->IsJSObject() &&
6015 JSObject::cast(proto)->map()->is_hidden_prototype()) {
6016 count++;
6017 proto = JSObject::cast(proto)->GetPrototype();
6018 }
6019 return count;
6020}
6021
6022
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006023static Object* DebugLookupResultValue(Object* receiver, String* name,
6024 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00006025 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006026 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006027 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006028 case NORMAL:
6029 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006030 if (value->IsTheHole()) {
6031 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006032 }
6033 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006034 case FIELD:
6035 value =
6036 JSObject::cast(
6037 result->holder())->FastPropertyAt(result->GetFieldIndex());
6038 if (value->IsTheHole()) {
6039 return Heap::undefined_value();
6040 }
6041 return value;
6042 case CONSTANT_FUNCTION:
6043 return result->GetConstantFunction();
6044 case CALLBACKS: {
6045 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006046 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006047 value = receiver->GetPropertyWithCallback(
6048 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00006049 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006050 value = Top::pending_exception();
6051 Top::clear_pending_exception();
6052 if (caught_exception != NULL) {
6053 *caught_exception = true;
6054 }
6055 }
6056 return value;
6057 } else {
6058 return Heap::undefined_value();
6059 }
6060 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006062 case MAP_TRANSITION:
6063 case CONSTANT_TRANSITION:
6064 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006065 return Heap::undefined_value();
6066 default:
6067 UNREACHABLE();
6068 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006069 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006070 return Heap::undefined_value();
6071}
6072
6073
ager@chromium.org32912102009-01-16 10:38:43 +00006074// Get debugger related details for an object property.
6075// args[0]: object holding property
6076// args[1]: name of the property
6077//
6078// The array returned contains the following information:
6079// 0: Property value
6080// 1: Property details
6081// 2: Property value is exception
6082// 3: Getter function if defined
6083// 4: Setter function if defined
6084// Items 2-4 are only filled if the property has either a getter or a setter
6085// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006086static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087 HandleScope scope;
6088
6089 ASSERT(args.length() == 2);
6090
6091 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6092 CONVERT_ARG_CHECKED(String, name, 1);
6093
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006094 // Make sure to set the current context to the context before the debugger was
6095 // entered (if the debugger is entered). The reason for switching context here
6096 // is that for some property lookups (accessors and interceptors) callbacks
6097 // into the embedding application can occour, and the embedding application
6098 // could have the assumption that its own global context is the current
6099 // context and not some internal debugger context.
6100 SaveContext save;
6101 if (Debug::InDebugger()) {
6102 Top::set_context(*Debug::debugger_entry()->GetContext());
6103 }
6104
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006105 // Skip the global proxy as it has no properties and always delegates to the
6106 // real global object.
6107 if (obj->IsJSGlobalProxy()) {
6108 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6109 }
6110
6111
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006112 // Check if the name is trivially convertible to an index and get the element
6113 // if so.
6114 uint32_t index;
6115 if (name->AsArrayIndex(&index)) {
6116 Handle<FixedArray> details = Factory::NewFixedArray(2);
6117 details->set(0, Runtime::GetElementOrCharAt(obj, index));
6118 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
6119 return *Factory::NewJSArrayWithElements(details);
6120 }
6121
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006122 // Find the number of objects making up this.
6123 int length = LocalPrototypeChainLength(*obj);
6124
6125 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006126 Handle<JSObject> jsproto = obj;
6127 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006128 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006129 jsproto->LocalLookup(*name, &result);
6130 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006131 // LookupResult is not GC safe as it holds raw object pointers.
6132 // GC can happen later in this code so put the required fields into
6133 // local variables using handles when required for later use.
6134 PropertyType result_type = result.type();
6135 Handle<Object> result_callback_obj;
6136 if (result_type == CALLBACKS) {
6137 result_callback_obj = Handle<Object>(result.GetCallbackObject());
6138 }
6139 Smi* property_details = result.GetPropertyDetails().AsSmi();
6140 // DebugLookupResultValue can cause GC so details from LookupResult needs
6141 // to be copied to handles before this.
6142 bool caught_exception = false;
6143 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
6144 &caught_exception);
6145 if (raw_value->IsFailure()) return raw_value;
6146 Handle<Object> value(raw_value);
6147
6148 // If the callback object is a fixed array then it contains JavaScript
6149 // getter and/or setter.
6150 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
6151 result_callback_obj->IsFixedArray();
6152 Handle<FixedArray> details =
6153 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
6154 details->set(0, *value);
6155 details->set(1, property_details);
6156 if (hasJavaScriptAccessors) {
6157 details->set(2,
6158 caught_exception ? Heap::true_value()
6159 : Heap::false_value());
6160 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
6161 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
6162 }
6163
6164 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006165 }
6166 if (i < length - 1) {
6167 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6168 }
6169 }
6170
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006171 return Heap::undefined_value();
6172}
6173
6174
6175static Object* Runtime_DebugGetProperty(Arguments args) {
6176 HandleScope scope;
6177
6178 ASSERT(args.length() == 2);
6179
6180 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6181 CONVERT_ARG_CHECKED(String, name, 1);
6182
6183 LookupResult result;
6184 obj->Lookup(*name, &result);
6185 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006186 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006187 }
6188 return Heap::undefined_value();
6189}
6190
6191
6192// Return the names of the local named properties.
6193// args[0]: object
6194static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
6195 HandleScope scope;
6196 ASSERT(args.length() == 1);
6197 if (!args[0]->IsJSObject()) {
6198 return Heap::undefined_value();
6199 }
6200 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6201
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006202 // Skip the global proxy as it has no properties and always delegates to the
6203 // real global object.
6204 if (obj->IsJSGlobalProxy()) {
6205 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6206 }
6207
6208 // Find the number of objects making up this.
6209 int length = LocalPrototypeChainLength(*obj);
6210
6211 // Find the number of local properties for each of the objects.
6212 int* local_property_count = NewArray<int>(length);
6213 int total_property_count = 0;
6214 Handle<JSObject> jsproto = obj;
6215 for (int i = 0; i < length; i++) {
6216 int n;
6217 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
6218 local_property_count[i] = n;
6219 total_property_count += n;
6220 if (i < length - 1) {
6221 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6222 }
6223 }
6224
6225 // Allocate an array with storage for all the property names.
6226 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
6227
6228 // Get the property names.
6229 jsproto = obj;
ager@chromium.orgc730f772009-11-11 10:11:16 +00006230 int proto_with_hidden_properties = 0;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006231 for (int i = 0; i < length; i++) {
6232 jsproto->GetLocalPropertyNames(*names,
6233 i == 0 ? 0 : local_property_count[i - 1]);
ager@chromium.orgc730f772009-11-11 10:11:16 +00006234 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
6235 proto_with_hidden_properties++;
6236 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006237 if (i < length - 1) {
6238 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6239 }
6240 }
6241
ager@chromium.orgc730f772009-11-11 10:11:16 +00006242 // Filter out name of hidden propeties object.
6243 if (proto_with_hidden_properties > 0) {
6244 Handle<FixedArray> old_names = names;
6245 names = Factory::NewFixedArray(
6246 names->length() - proto_with_hidden_properties);
6247 int dest_pos = 0;
6248 for (int i = 0; i < total_property_count; i++) {
6249 Object* name = old_names->get(i);
6250 if (name == Heap::hidden_symbol()) {
6251 continue;
6252 }
6253 names->set(dest_pos++, name);
6254 }
6255 }
6256
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006257 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006258 return *Factory::NewJSArrayWithElements(names);
6259}
6260
6261
6262// Return the names of the local indexed properties.
6263// args[0]: object
6264static Object* Runtime_DebugLocalElementNames(Arguments args) {
6265 HandleScope scope;
6266 ASSERT(args.length() == 1);
6267 if (!args[0]->IsJSObject()) {
6268 return Heap::undefined_value();
6269 }
6270 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6271
6272 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
6273 Handle<FixedArray> names = Factory::NewFixedArray(n);
6274 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
6275 return *Factory::NewJSArrayWithElements(names);
6276}
6277
6278
6279// Return the property type calculated from the property details.
6280// args[0]: smi with property details.
6281static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6282 ASSERT(args.length() == 1);
6283 CONVERT_CHECKED(Smi, details, args[0]);
6284 PropertyType type = PropertyDetails(details).type();
6285 return Smi::FromInt(static_cast<int>(type));
6286}
6287
6288
6289// Return the property attribute calculated from the property details.
6290// args[0]: smi with property details.
6291static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6292 ASSERT(args.length() == 1);
6293 CONVERT_CHECKED(Smi, details, args[0]);
6294 PropertyAttributes attributes = PropertyDetails(details).attributes();
6295 return Smi::FromInt(static_cast<int>(attributes));
6296}
6297
6298
6299// Return the property insertion index calculated from the property details.
6300// args[0]: smi with property details.
6301static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6302 ASSERT(args.length() == 1);
6303 CONVERT_CHECKED(Smi, details, args[0]);
6304 int index = PropertyDetails(details).index();
6305 return Smi::FromInt(index);
6306}
6307
6308
6309// Return information on whether an object has a named or indexed interceptor.
6310// args[0]: object
6311static Object* Runtime_DebugInterceptorInfo(Arguments args) {
6312 HandleScope scope;
6313 ASSERT(args.length() == 1);
6314 if (!args[0]->IsJSObject()) {
6315 return Smi::FromInt(0);
6316 }
6317 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6318
6319 int result = 0;
6320 if (obj->HasNamedInterceptor()) result |= 2;
6321 if (obj->HasIndexedInterceptor()) result |= 1;
6322
6323 return Smi::FromInt(result);
6324}
6325
6326
6327// Return property names from named interceptor.
6328// args[0]: object
6329static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
6330 HandleScope scope;
6331 ASSERT(args.length() == 1);
6332 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006333
ager@chromium.org32912102009-01-16 10:38:43 +00006334 if (obj->HasNamedInterceptor()) {
6335 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
6336 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6337 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006338 return Heap::undefined_value();
6339}
6340
6341
6342// Return element names from indexed interceptor.
6343// args[0]: object
6344static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
6345 HandleScope scope;
6346 ASSERT(args.length() == 1);
6347 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006348
ager@chromium.org32912102009-01-16 10:38:43 +00006349 if (obj->HasIndexedInterceptor()) {
6350 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
6351 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6352 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006353 return Heap::undefined_value();
6354}
6355
6356
6357// Return property value from named interceptor.
6358// args[0]: object
6359// args[1]: property name
6360static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6361 HandleScope scope;
6362 ASSERT(args.length() == 2);
6363 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6364 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6365 CONVERT_ARG_CHECKED(String, name, 1);
6366
6367 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006368 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006369}
6370
6371
6372// Return element value from indexed interceptor.
6373// args[0]: object
6374// args[1]: index
6375static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6376 HandleScope scope;
6377 ASSERT(args.length() == 2);
6378 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6379 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6380 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6381
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006382 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006383}
6384
6385
6386static Object* Runtime_CheckExecutionState(Arguments args) {
6387 ASSERT(args.length() >= 1);
6388 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006389 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006390 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006391 return Top::Throw(Heap::illegal_execution_state_symbol());
6392 }
6393
6394 return Heap::true_value();
6395}
6396
6397
6398static Object* Runtime_GetFrameCount(Arguments args) {
6399 HandleScope scope;
6400 ASSERT(args.length() == 1);
6401
6402 // Check arguments.
6403 Object* result = Runtime_CheckExecutionState(args);
6404 if (result->IsFailure()) return result;
6405
6406 // Count all frames which are relevant to debugging stack trace.
6407 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006408 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006409 if (id == StackFrame::NO_ID) {
6410 // If there is no JavaScript stack frame count is 0.
6411 return Smi::FromInt(0);
6412 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006413 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6414 return Smi::FromInt(n);
6415}
6416
6417
6418static const int kFrameDetailsFrameIdIndex = 0;
6419static const int kFrameDetailsReceiverIndex = 1;
6420static const int kFrameDetailsFunctionIndex = 2;
6421static const int kFrameDetailsArgumentCountIndex = 3;
6422static const int kFrameDetailsLocalCountIndex = 4;
6423static const int kFrameDetailsSourcePositionIndex = 5;
6424static const int kFrameDetailsConstructCallIndex = 6;
6425static const int kFrameDetailsDebuggerFrameIndex = 7;
6426static const int kFrameDetailsFirstDynamicIndex = 8;
6427
6428// Return an array with frame details
6429// args[0]: number: break id
6430// args[1]: number: frame index
6431//
6432// The array returned contains the following information:
6433// 0: Frame id
6434// 1: Receiver
6435// 2: Function
6436// 3: Argument count
6437// 4: Local count
6438// 5: Source position
6439// 6: Constructor call
6440// 7: Debugger frame
6441// Arguments name, value
6442// Locals name, value
6443static Object* Runtime_GetFrameDetails(Arguments args) {
6444 HandleScope scope;
6445 ASSERT(args.length() == 2);
6446
6447 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006448 Object* check = Runtime_CheckExecutionState(args);
6449 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006450 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6451
6452 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006453 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006454 if (id == StackFrame::NO_ID) {
6455 // If there are no JavaScript stack frames return undefined.
6456 return Heap::undefined_value();
6457 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006458 int count = 0;
6459 JavaScriptFrameIterator it(id);
6460 for (; !it.done(); it.Advance()) {
6461 if (count == index) break;
6462 count++;
6463 }
6464 if (it.done()) return Heap::undefined_value();
6465
6466 // Traverse the saved contexts chain to find the active context for the
6467 // selected frame.
6468 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006469 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006470 save = save->prev();
6471 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006472 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006473
6474 // Get the frame id.
6475 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6476
6477 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006478 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006479
6480 // Check for constructor frame.
6481 bool constructor = it.frame()->IsConstructor();
6482
6483 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006484 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006485 ScopeInfo<> info(*code);
6486
6487 // Get the context.
6488 Handle<Context> context(Context::cast(it.frame()->context()));
6489
6490 // Get the locals names and values into a temporary array.
6491 //
6492 // TODO(1240907): Hide compiler-introduced stack variables
6493 // (e.g. .result)? For users of the debugger, they will probably be
6494 // confusing.
6495 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6496 for (int i = 0; i < info.NumberOfLocals(); i++) {
6497 // Name of the local.
6498 locals->set(i * 2, *info.LocalName(i));
6499
6500 // Fetch the value of the local - either from the stack or from a
6501 // heap-allocated context.
6502 if (i < info.number_of_stack_slots()) {
6503 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6504 } else {
6505 Handle<String> name = info.LocalName(i);
6506 // Traverse the context chain to the function context as all local
6507 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006508 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006509 context = Handle<Context>(context->previous());
6510 }
6511 ASSERT(context->is_function_context());
6512 locals->set(i * 2 + 1,
6513 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6514 NULL)));
6515 }
6516 }
6517
6518 // Now advance to the arguments adapter frame (if any). If contains all
6519 // the provided parameters and
6520
6521 // Now advance to the arguments adapter frame (if any). It contains all
6522 // the provided parameters whereas the function frame always have the number
6523 // of arguments matching the functions parameters. The rest of the
6524 // information (except for what is collected above) is the same.
6525 it.AdvanceToArgumentsFrame();
6526
6527 // Find the number of arguments to fill. At least fill the number of
6528 // parameters for the function and fill more if more parameters are provided.
6529 int argument_count = info.number_of_parameters();
6530 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6531 argument_count = it.frame()->GetProvidedParametersCount();
6532 }
6533
6534 // Calculate the size of the result.
6535 int details_size = kFrameDetailsFirstDynamicIndex +
6536 2 * (argument_count + info.NumberOfLocals());
6537 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6538
6539 // Add the frame id.
6540 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6541
6542 // Add the function (same as in function frame).
6543 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6544
6545 // Add the arguments count.
6546 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6547
6548 // Add the locals count
6549 details->set(kFrameDetailsLocalCountIndex,
6550 Smi::FromInt(info.NumberOfLocals()));
6551
6552 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006553 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006554 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6555 } else {
6556 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6557 }
6558
6559 // Add the constructor information.
6560 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6561
6562 // Add information on whether this frame is invoked in the debugger context.
6563 details->set(kFrameDetailsDebuggerFrameIndex,
6564 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6565
6566 // Fill the dynamic part.
6567 int details_index = kFrameDetailsFirstDynamicIndex;
6568
6569 // Add arguments name and value.
6570 for (int i = 0; i < argument_count; i++) {
6571 // Name of the argument.
6572 if (i < info.number_of_parameters()) {
6573 details->set(details_index++, *info.parameter_name(i));
6574 } else {
6575 details->set(details_index++, Heap::undefined_value());
6576 }
6577
6578 // Parameter value.
6579 if (i < it.frame()->GetProvidedParametersCount()) {
6580 details->set(details_index++, it.frame()->GetParameter(i));
6581 } else {
6582 details->set(details_index++, Heap::undefined_value());
6583 }
6584 }
6585
6586 // Add locals name and value from the temporary copy from the function frame.
6587 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6588 details->set(details_index++, locals->get(i));
6589 }
6590
6591 // Add the receiver (same as in function frame).
6592 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6593 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6594 Handle<Object> receiver(it.frame()->receiver());
6595 if (!receiver->IsJSObject()) {
6596 // If the receiver is NOT a JSObject we have hit an optimization
6597 // where a value object is not converted into a wrapped JS objects.
6598 // To hide this optimization from the debugger, we wrap the receiver
6599 // by creating correct wrapper object based on the calling frame's
6600 // global context.
6601 it.Advance();
6602 Handle<Context> calling_frames_global_context(
6603 Context::cast(Context::cast(it.frame()->context())->global_context()));
6604 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6605 }
6606 details->set(kFrameDetailsReceiverIndex, *receiver);
6607
6608 ASSERT_EQ(details_size, details_index);
6609 return *Factory::NewJSArrayWithElements(details);
6610}
6611
6612
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006613// Copy all the context locals into an object used to materialize a scope.
6614static void CopyContextLocalsToScopeObject(Handle<Code> code,
6615 ScopeInfo<>& scope_info,
6616 Handle<Context> context,
6617 Handle<JSObject> scope_object) {
6618 // Fill all context locals to the context extension.
6619 for (int i = Context::MIN_CONTEXT_SLOTS;
6620 i < scope_info.number_of_context_slots();
6621 i++) {
6622 int context_index =
6623 ScopeInfo<>::ContextSlotIndex(*code,
6624 *scope_info.context_slot_name(i),
6625 NULL);
6626
6627 // Don't include the arguments shadow (.arguments) context variable.
6628 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6629 SetProperty(scope_object,
6630 scope_info.context_slot_name(i),
6631 Handle<Object>(context->get(context_index)), NONE);
6632 }
6633 }
6634}
6635
6636
6637// Create a plain JSObject which materializes the local scope for the specified
6638// frame.
6639static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6640 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6641 Handle<Code> code(function->code());
6642 ScopeInfo<> scope_info(*code);
6643
6644 // Allocate and initialize a JSObject with all the arguments, stack locals
6645 // heap locals and extension properties of the debugged function.
6646 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6647
6648 // First fill all parameters.
6649 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6650 SetProperty(local_scope,
6651 scope_info.parameter_name(i),
6652 Handle<Object>(frame->GetParameter(i)), NONE);
6653 }
6654
6655 // Second fill all stack locals.
6656 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6657 SetProperty(local_scope,
6658 scope_info.stack_slot_name(i),
6659 Handle<Object>(frame->GetExpression(i)), NONE);
6660 }
6661
6662 // Third fill all context locals.
6663 Handle<Context> frame_context(Context::cast(frame->context()));
6664 Handle<Context> function_context(frame_context->fcontext());
6665 CopyContextLocalsToScopeObject(code, scope_info,
6666 function_context, local_scope);
6667
6668 // Finally copy any properties from the function context extension. This will
6669 // be variables introduced by eval.
6670 if (function_context->closure() == *function) {
6671 if (function_context->has_extension() &&
6672 !function_context->IsGlobalContext()) {
6673 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006674 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006675 for (int i = 0; i < keys->length(); i++) {
6676 // Names of variables introduced by eval are strings.
6677 ASSERT(keys->get(i)->IsString());
6678 Handle<String> key(String::cast(keys->get(i)));
6679 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6680 }
6681 }
6682 }
6683 return local_scope;
6684}
6685
6686
6687// Create a plain JSObject which materializes the closure content for the
6688// context.
6689static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6690 ASSERT(context->is_function_context());
6691
6692 Handle<Code> code(context->closure()->code());
6693 ScopeInfo<> scope_info(*code);
6694
6695 // Allocate and initialize a JSObject with all the content of theis function
6696 // closure.
6697 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6698
6699 // Check whether the arguments shadow object exists.
6700 int arguments_shadow_index =
6701 ScopeInfo<>::ContextSlotIndex(*code,
6702 Heap::arguments_shadow_symbol(),
6703 NULL);
6704 if (arguments_shadow_index >= 0) {
6705 // In this case all the arguments are available in the arguments shadow
6706 // object.
6707 Handle<JSObject> arguments_shadow(
6708 JSObject::cast(context->get(arguments_shadow_index)));
6709 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6710 SetProperty(closure_scope,
6711 scope_info.parameter_name(i),
6712 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6713 }
6714 }
6715
6716 // Fill all context locals to the context extension.
6717 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6718
6719 // Finally copy any properties from the function context extension. This will
6720 // be variables introduced by eval.
6721 if (context->has_extension()) {
6722 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006723 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006724 for (int i = 0; i < keys->length(); i++) {
6725 // Names of variables introduced by eval are strings.
6726 ASSERT(keys->get(i)->IsString());
6727 Handle<String> key(String::cast(keys->get(i)));
6728 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6729 }
6730 }
6731
6732 return closure_scope;
6733}
6734
6735
6736// Iterate over the actual scopes visible from a stack frame. All scopes are
6737// backed by an actual context except the local scope, which is inserted
6738// "artifically" in the context chain.
6739class ScopeIterator {
6740 public:
6741 enum ScopeType {
6742 ScopeTypeGlobal = 0,
6743 ScopeTypeLocal,
6744 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006745 ScopeTypeClosure,
6746 // Every catch block contains an implicit with block (its parameter is
6747 // a JSContextExtensionObject) that extends current scope with a variable
6748 // holding exception object. Such with blocks are treated as scopes of their
6749 // own type.
6750 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006751 };
6752
6753 explicit ScopeIterator(JavaScriptFrame* frame)
6754 : frame_(frame),
6755 function_(JSFunction::cast(frame->function())),
6756 context_(Context::cast(frame->context())),
6757 local_done_(false),
6758 at_local_(false) {
6759
6760 // Check whether the first scope is actually a local scope.
6761 if (context_->IsGlobalContext()) {
6762 // If there is a stack slot for .result then this local scope has been
6763 // created for evaluating top level code and it is not a real local scope.
6764 // Checking for the existence of .result seems fragile, but the scope info
6765 // saved with the code object does not otherwise have that information.
6766 Handle<Code> code(function_->code());
6767 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6768 at_local_ = index < 0;
6769 } else if (context_->is_function_context()) {
6770 at_local_ = true;
6771 }
6772 }
6773
6774 // More scopes?
6775 bool Done() { return context_.is_null(); }
6776
6777 // Move to the next scope.
6778 void Next() {
6779 // If at a local scope mark the local scope as passed.
6780 if (at_local_) {
6781 at_local_ = false;
6782 local_done_ = true;
6783
6784 // If the current context is not associated with the local scope the
6785 // current context is the next real scope, so don't move to the next
6786 // context in this case.
6787 if (context_->closure() != *function_) {
6788 return;
6789 }
6790 }
6791
6792 // The global scope is always the last in the chain.
6793 if (context_->IsGlobalContext()) {
6794 context_ = Handle<Context>();
6795 return;
6796 }
6797
6798 // Move to the next context.
6799 if (context_->is_function_context()) {
6800 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6801 } else {
6802 context_ = Handle<Context>(context_->previous());
6803 }
6804
6805 // If passing the local scope indicate that the current scope is now the
6806 // local scope.
6807 if (!local_done_ &&
6808 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6809 at_local_ = true;
6810 }
6811 }
6812
6813 // Return the type of the current scope.
6814 int Type() {
6815 if (at_local_) {
6816 return ScopeTypeLocal;
6817 }
6818 if (context_->IsGlobalContext()) {
6819 ASSERT(context_->global()->IsGlobalObject());
6820 return ScopeTypeGlobal;
6821 }
6822 if (context_->is_function_context()) {
6823 return ScopeTypeClosure;
6824 }
6825 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006826 // Current scope is either an explicit with statement or a with statement
6827 // implicitely generated for a catch block.
6828 // If the extension object here is a JSContextExtensionObject then
6829 // current with statement is one frome a catch block otherwise it's a
6830 // regular with statement.
6831 if (context_->extension()->IsJSContextExtensionObject()) {
6832 return ScopeTypeCatch;
6833 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006834 return ScopeTypeWith;
6835 }
6836
6837 // Return the JavaScript object with the content of the current scope.
6838 Handle<JSObject> ScopeObject() {
6839 switch (Type()) {
6840 case ScopeIterator::ScopeTypeGlobal:
6841 return Handle<JSObject>(CurrentContext()->global());
6842 break;
6843 case ScopeIterator::ScopeTypeLocal:
6844 // Materialize the content of the local scope into a JSObject.
6845 return MaterializeLocalScope(frame_);
6846 break;
6847 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006848 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006849 // Return the with object.
6850 return Handle<JSObject>(CurrentContext()->extension());
6851 break;
6852 case ScopeIterator::ScopeTypeClosure:
6853 // Materialize the content of the closure scope into a JSObject.
6854 return MaterializeClosure(CurrentContext());
6855 break;
6856 }
6857 UNREACHABLE();
6858 return Handle<JSObject>();
6859 }
6860
6861 // Return the context for this scope. For the local context there might not
6862 // be an actual context.
6863 Handle<Context> CurrentContext() {
6864 if (at_local_ && context_->closure() != *function_) {
6865 return Handle<Context>();
6866 }
6867 return context_;
6868 }
6869
6870#ifdef DEBUG
6871 // Debug print of the content of the current scope.
6872 void DebugPrint() {
6873 switch (Type()) {
6874 case ScopeIterator::ScopeTypeGlobal:
6875 PrintF("Global:\n");
6876 CurrentContext()->Print();
6877 break;
6878
6879 case ScopeIterator::ScopeTypeLocal: {
6880 PrintF("Local:\n");
6881 Handle<Code> code(function_->code());
6882 ScopeInfo<> scope_info(*code);
6883 scope_info.Print();
6884 if (!CurrentContext().is_null()) {
6885 CurrentContext()->Print();
6886 if (CurrentContext()->has_extension()) {
6887 Handle<JSObject> extension =
6888 Handle<JSObject>(CurrentContext()->extension());
6889 if (extension->IsJSContextExtensionObject()) {
6890 extension->Print();
6891 }
6892 }
6893 }
6894 break;
6895 }
6896
6897 case ScopeIterator::ScopeTypeWith: {
6898 PrintF("With:\n");
6899 Handle<JSObject> extension =
6900 Handle<JSObject>(CurrentContext()->extension());
6901 extension->Print();
6902 break;
6903 }
6904
ager@chromium.orga1645e22009-09-09 19:27:10 +00006905 case ScopeIterator::ScopeTypeCatch: {
6906 PrintF("Catch:\n");
6907 Handle<JSObject> extension =
6908 Handle<JSObject>(CurrentContext()->extension());
6909 extension->Print();
6910 break;
6911 }
6912
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006913 case ScopeIterator::ScopeTypeClosure: {
6914 PrintF("Closure:\n");
6915 CurrentContext()->Print();
6916 if (CurrentContext()->has_extension()) {
6917 Handle<JSObject> extension =
6918 Handle<JSObject>(CurrentContext()->extension());
6919 if (extension->IsJSContextExtensionObject()) {
6920 extension->Print();
6921 }
6922 }
6923 break;
6924 }
6925
6926 default:
6927 UNREACHABLE();
6928 }
6929 PrintF("\n");
6930 }
6931#endif
6932
6933 private:
6934 JavaScriptFrame* frame_;
6935 Handle<JSFunction> function_;
6936 Handle<Context> context_;
6937 bool local_done_;
6938 bool at_local_;
6939
6940 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6941};
6942
6943
6944static Object* Runtime_GetScopeCount(Arguments args) {
6945 HandleScope scope;
6946 ASSERT(args.length() == 2);
6947
6948 // Check arguments.
6949 Object* check = Runtime_CheckExecutionState(args);
6950 if (check->IsFailure()) return check;
6951 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6952
6953 // Get the frame where the debugging is performed.
6954 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6955 JavaScriptFrameIterator it(id);
6956 JavaScriptFrame* frame = it.frame();
6957
6958 // Count the visible scopes.
6959 int n = 0;
6960 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6961 n++;
6962 }
6963
6964 return Smi::FromInt(n);
6965}
6966
6967
6968static const int kScopeDetailsTypeIndex = 0;
6969static const int kScopeDetailsObjectIndex = 1;
6970static const int kScopeDetailsSize = 2;
6971
6972// Return an array with scope details
6973// args[0]: number: break id
6974// args[1]: number: frame index
6975// args[2]: number: scope index
6976//
6977// The array returned contains the following information:
6978// 0: Scope type
6979// 1: Scope object
6980static Object* Runtime_GetScopeDetails(Arguments args) {
6981 HandleScope scope;
6982 ASSERT(args.length() == 3);
6983
6984 // Check arguments.
6985 Object* check = Runtime_CheckExecutionState(args);
6986 if (check->IsFailure()) return check;
6987 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6988 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6989
6990 // Get the frame where the debugging is performed.
6991 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6992 JavaScriptFrameIterator frame_it(id);
6993 JavaScriptFrame* frame = frame_it.frame();
6994
6995 // Find the requested scope.
6996 int n = 0;
6997 ScopeIterator it(frame);
6998 for (; !it.Done() && n < index; it.Next()) {
6999 n++;
7000 }
7001 if (it.Done()) {
7002 return Heap::undefined_value();
7003 }
7004
7005 // Calculate the size of the result.
7006 int details_size = kScopeDetailsSize;
7007 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
7008
7009 // Fill in scope details.
7010 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
7011 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
7012
7013 return *Factory::NewJSArrayWithElements(details);
7014}
7015
7016
7017static Object* Runtime_DebugPrintScopes(Arguments args) {
7018 HandleScope scope;
7019 ASSERT(args.length() == 0);
7020
7021#ifdef DEBUG
7022 // Print the scopes for the top frame.
7023 StackFrameLocator locator;
7024 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
7025 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
7026 it.DebugPrint();
7027 }
7028#endif
7029 return Heap::undefined_value();
7030}
7031
7032
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007033static Object* Runtime_GetCFrames(Arguments args) {
7034 HandleScope scope;
7035 ASSERT(args.length() == 1);
7036 Object* result = Runtime_CheckExecutionState(args);
7037 if (result->IsFailure()) return result;
7038
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007039#if V8_HOST_ARCH_64_BIT
7040 UNIMPLEMENTED();
7041 return Heap::undefined_value();
7042#else
7043
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007044 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007045 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
7046 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007047 if (frames_count == OS::kStackWalkError) {
7048 return Heap::undefined_value();
7049 }
7050
7051 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
7052 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
7053 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
7054 for (int i = 0; i < frames_count; i++) {
7055 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
7056 frame_value->SetProperty(
7057 *address_str,
7058 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
7059 NONE);
7060
7061 // Get the stack walk text for this frame.
7062 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007063 int frame_text_length = StrLength(frames[i].text);
7064 if (frame_text_length > 0) {
7065 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007066 frame_text = Factory::NewStringFromAscii(str);
7067 }
7068
7069 if (!frame_text.is_null()) {
7070 frame_value->SetProperty(*text_str, *frame_text, NONE);
7071 }
7072
7073 frames_array->set(i, *frame_value);
7074 }
7075 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007076#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007077}
7078
7079
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007080static Object* Runtime_GetThreadCount(Arguments args) {
7081 HandleScope scope;
7082 ASSERT(args.length() == 1);
7083
7084 // Check arguments.
7085 Object* result = Runtime_CheckExecutionState(args);
7086 if (result->IsFailure()) return result;
7087
7088 // Count all archived V8 threads.
7089 int n = 0;
7090 for (ThreadState* thread = ThreadState::FirstInUse();
7091 thread != NULL;
7092 thread = thread->Next()) {
7093 n++;
7094 }
7095
7096 // Total number of threads is current thread and archived threads.
7097 return Smi::FromInt(n + 1);
7098}
7099
7100
7101static const int kThreadDetailsCurrentThreadIndex = 0;
7102static const int kThreadDetailsThreadIdIndex = 1;
7103static const int kThreadDetailsSize = 2;
7104
7105// Return an array with thread details
7106// args[0]: number: break id
7107// args[1]: number: thread index
7108//
7109// The array returned contains the following information:
7110// 0: Is current thread?
7111// 1: Thread id
7112static Object* Runtime_GetThreadDetails(Arguments args) {
7113 HandleScope scope;
7114 ASSERT(args.length() == 2);
7115
7116 // Check arguments.
7117 Object* check = Runtime_CheckExecutionState(args);
7118 if (check->IsFailure()) return check;
7119 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
7120
7121 // Allocate array for result.
7122 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
7123
7124 // Thread index 0 is current thread.
7125 if (index == 0) {
7126 // Fill the details.
7127 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
7128 details->set(kThreadDetailsThreadIdIndex,
7129 Smi::FromInt(ThreadManager::CurrentId()));
7130 } else {
7131 // Find the thread with the requested index.
7132 int n = 1;
7133 ThreadState* thread = ThreadState::FirstInUse();
7134 while (index != n && thread != NULL) {
7135 thread = thread->Next();
7136 n++;
7137 }
7138 if (thread == NULL) {
7139 return Heap::undefined_value();
7140 }
7141
7142 // Fill the details.
7143 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
7144 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
7145 }
7146
7147 // Convert to JS array and return.
7148 return *Factory::NewJSArrayWithElements(details);
7149}
7150
7151
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007152static Object* Runtime_GetBreakLocations(Arguments args) {
7153 HandleScope scope;
7154 ASSERT(args.length() == 1);
7155
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007156 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7157 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007158 // Find the number of break points
7159 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
7160 if (break_locations->IsUndefined()) return Heap::undefined_value();
7161 // Return array as JS array
7162 return *Factory::NewJSArrayWithElements(
7163 Handle<FixedArray>::cast(break_locations));
7164}
7165
7166
7167// Set a break point in a function
7168// args[0]: function
7169// args[1]: number: break source position (within the function source)
7170// args[2]: number: break point object
7171static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
7172 HandleScope scope;
7173 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007174 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7175 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007176 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7177 RUNTIME_ASSERT(source_position >= 0);
7178 Handle<Object> break_point_object_arg = args.at<Object>(2);
7179
7180 // Set break point.
7181 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
7182
7183 return Heap::undefined_value();
7184}
7185
7186
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007187Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
7188 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007189 // Iterate the heap looking for SharedFunctionInfo generated from the
7190 // script. The inner most SharedFunctionInfo containing the source position
7191 // for the requested break point is found.
7192 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
7193 // which is found is not compiled it is compiled and the heap is iterated
7194 // again as the compilation might create inner functions from the newly
7195 // compiled function and the actual requested break point might be in one of
7196 // these functions.
7197 bool done = false;
7198 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00007199 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007200 Handle<SharedFunctionInfo> target;
7201 // The current candidate for the last function in script:
7202 Handle<SharedFunctionInfo> last;
7203 while (!done) {
7204 HeapIterator iterator;
7205 while (iterator.has_next()) {
7206 HeapObject* obj = iterator.next();
7207 ASSERT(obj != NULL);
7208 if (obj->IsSharedFunctionInfo()) {
7209 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
7210 if (shared->script() == *script) {
7211 // If the SharedFunctionInfo found has the requested script data and
7212 // contains the source position it is a candidate.
7213 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00007214 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007215 start_position = shared->start_position();
7216 }
7217 if (start_position <= position &&
7218 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007219 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007220 // candidate this is the new candidate.
7221 if (target.is_null()) {
7222 target_start_position = start_position;
7223 target = shared;
7224 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007225 if (target_start_position == start_position &&
7226 shared->end_position() == target->end_position()) {
7227 // If a top-level function contain only one function
7228 // declartion the source for the top-level and the function is
7229 // the same. In that case prefer the non top-level function.
7230 if (!shared->is_toplevel()) {
7231 target_start_position = start_position;
7232 target = shared;
7233 }
7234 } else if (target_start_position <= start_position &&
7235 shared->end_position() <= target->end_position()) {
7236 // This containment check includes equality as a function inside
7237 // a top-level function can share either start or end position
7238 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007239 target_start_position = start_position;
7240 target = shared;
7241 }
7242 }
7243 }
7244
7245 // Keep track of the last function in the script.
7246 if (last.is_null() ||
7247 shared->end_position() > last->start_position()) {
7248 last = shared;
7249 }
7250 }
7251 }
7252 }
7253
7254 // Make sure some candidate is selected.
7255 if (target.is_null()) {
7256 if (!last.is_null()) {
7257 // Position after the last function - use last.
7258 target = last;
7259 } else {
7260 // Unable to find function - possibly script without any function.
7261 return Heap::undefined_value();
7262 }
7263 }
7264
7265 // If the candidate found is compiled we are done. NOTE: when lazy
7266 // compilation of inner functions is introduced some additional checking
7267 // needs to be done here to compile inner functions.
7268 done = target->is_compiled();
7269 if (!done) {
7270 // If the candidate is not compiled compile it to reveal any inner
7271 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007272 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007273 }
7274 }
7275
7276 return *target;
7277}
7278
7279
7280// Change the state of a break point in a script. NOTE: Regarding performance
7281// see the NOTE for GetScriptFromScriptData.
7282// args[0]: script to set break point in
7283// args[1]: number: break source position (within the script source)
7284// args[2]: number: break point object
7285static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7286 HandleScope scope;
7287 ASSERT(args.length() == 3);
7288 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7289 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7290 RUNTIME_ASSERT(source_position >= 0);
7291 Handle<Object> break_point_object_arg = args.at<Object>(2);
7292
7293 // Get the script from the script wrapper.
7294 RUNTIME_ASSERT(wrapper->value()->IsScript());
7295 Handle<Script> script(Script::cast(wrapper->value()));
7296
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007297 Object* result = Runtime::FindSharedFunctionInfoInScript(
7298 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007299 if (!result->IsUndefined()) {
7300 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7301 // Find position within function. The script position might be before the
7302 // source position of the first function.
7303 int position;
7304 if (shared->start_position() > source_position) {
7305 position = 0;
7306 } else {
7307 position = source_position - shared->start_position();
7308 }
7309 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7310 }
7311 return Heap::undefined_value();
7312}
7313
7314
7315// Clear a break point
7316// args[0]: number: break point object
7317static Object* Runtime_ClearBreakPoint(Arguments args) {
7318 HandleScope scope;
7319 ASSERT(args.length() == 1);
7320 Handle<Object> break_point_object_arg = args.at<Object>(0);
7321
7322 // Clear break point.
7323 Debug::ClearBreakPoint(break_point_object_arg);
7324
7325 return Heap::undefined_value();
7326}
7327
7328
7329// Change the state of break on exceptions
7330// args[0]: boolean indicating uncaught exceptions
7331// args[1]: boolean indicating on/off
7332static Object* Runtime_ChangeBreakOnException(Arguments args) {
7333 HandleScope scope;
7334 ASSERT(args.length() == 2);
7335 ASSERT(args[0]->IsNumber());
7336 ASSERT(args[1]->IsBoolean());
7337
7338 // Update break point state
7339 ExceptionBreakType type =
7340 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7341 bool enable = args[1]->ToBoolean()->IsTrue();
7342 Debug::ChangeBreakOnException(type, enable);
7343 return Heap::undefined_value();
7344}
7345
7346
7347// Prepare for stepping
7348// args[0]: break id for checking execution state
7349// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007350// args[2]: number of times to perform the step, for step out it is the number
7351// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007352static Object* Runtime_PrepareStep(Arguments args) {
7353 HandleScope scope;
7354 ASSERT(args.length() == 3);
7355 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007356 Object* check = Runtime_CheckExecutionState(args);
7357 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007358 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7359 return Top::Throw(Heap::illegal_argument_symbol());
7360 }
7361
7362 // Get the step action and check validity.
7363 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7364 if (step_action != StepIn &&
7365 step_action != StepNext &&
7366 step_action != StepOut &&
7367 step_action != StepInMin &&
7368 step_action != StepMin) {
7369 return Top::Throw(Heap::illegal_argument_symbol());
7370 }
7371
7372 // Get the number of steps.
7373 int step_count = NumberToInt32(args[2]);
7374 if (step_count < 1) {
7375 return Top::Throw(Heap::illegal_argument_symbol());
7376 }
7377
ager@chromium.orga1645e22009-09-09 19:27:10 +00007378 // Clear all current stepping setup.
7379 Debug::ClearStepping();
7380
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007381 // Prepare step.
7382 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7383 return Heap::undefined_value();
7384}
7385
7386
7387// Clear all stepping set by PrepareStep.
7388static Object* Runtime_ClearStepping(Arguments args) {
7389 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007390 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007391 Debug::ClearStepping();
7392 return Heap::undefined_value();
7393}
7394
7395
7396// Creates a copy of the with context chain. The copy of the context chain is
7397// is linked to the function context supplied.
7398static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7399 Handle<Context> function_context) {
7400 // At the bottom of the chain. Return the function context to link to.
7401 if (context_chain->is_function_context()) {
7402 return function_context;
7403 }
7404
7405 // Recursively copy the with contexts.
7406 Handle<Context> previous(context_chain->previous());
7407 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7408 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007409 CopyWithContextChain(function_context, previous),
7410 extension,
7411 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007412}
7413
7414
7415// Helper function to find or create the arguments object for
7416// Runtime_DebugEvaluate.
7417static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7418 Handle<JSFunction> function,
7419 Handle<Code> code,
7420 const ScopeInfo<>* sinfo,
7421 Handle<Context> function_context) {
7422 // Try to find the value of 'arguments' to pass as parameter. If it is not
7423 // found (that is the debugged function does not reference 'arguments' and
7424 // does not support eval) then create an 'arguments' object.
7425 int index;
7426 if (sinfo->number_of_stack_slots() > 0) {
7427 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7428 if (index != -1) {
7429 return Handle<Object>(frame->GetExpression(index));
7430 }
7431 }
7432
7433 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7434 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7435 NULL);
7436 if (index != -1) {
7437 return Handle<Object>(function_context->get(index));
7438 }
7439 }
7440
7441 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007442 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7443 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007444 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007445 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007446 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007447 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007448 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007449 return arguments;
7450}
7451
7452
7453// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007454// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007455// extension part has all the parameters and locals of the function on the
7456// stack frame. A function which calls eval with the code to evaluate is then
7457// compiled in this context and called in this context. As this context
7458// replaces the context of the function on the stack frame a new (empty)
7459// function is created as well to be used as the closure for the context.
7460// This function and the context acts as replacements for the function on the
7461// stack frame presenting the same view of the values of parameters and
7462// local variables as if the piece of JavaScript was evaluated at the point
7463// where the function on the stack frame is currently stopped.
7464static Object* Runtime_DebugEvaluate(Arguments args) {
7465 HandleScope scope;
7466
7467 // Check the execution state and decode arguments frame and source to be
7468 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007469 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007470 Object* check_result = Runtime_CheckExecutionState(args);
7471 if (check_result->IsFailure()) return check_result;
7472 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7473 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007474 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7475
7476 // Handle the processing of break.
7477 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007478
7479 // Get the frame where the debugging is performed.
7480 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7481 JavaScriptFrameIterator it(id);
7482 JavaScriptFrame* frame = it.frame();
7483 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7484 Handle<Code> code(function->code());
7485 ScopeInfo<> sinfo(*code);
7486
7487 // Traverse the saved contexts chain to find the active context for the
7488 // selected frame.
7489 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007490 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007491 save = save->prev();
7492 }
7493 ASSERT(save != NULL);
7494 SaveContext savex;
7495 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007496
7497 // Create the (empty) function replacing the function on the stack frame for
7498 // the purpose of evaluating in the context created below. It is important
7499 // that this function does not describe any parameters and local variables
7500 // in the context. If it does then this will cause problems with the lookup
7501 // in Context::Lookup, where context slots for parameters and local variables
7502 // are looked at before the extension object.
7503 Handle<JSFunction> go_between =
7504 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7505 go_between->set_context(function->context());
7506#ifdef DEBUG
7507 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7508 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7509 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7510#endif
7511
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007512 // Materialize the content of the local scope into a JSObject.
7513 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007514
7515 // Allocate a new context for the debug evaluation and set the extension
7516 // object build.
7517 Handle<Context> context =
7518 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007519 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007520 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007521 Handle<Context> frame_context(Context::cast(frame->context()));
7522 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007523 context = CopyWithContextChain(frame_context, context);
7524
7525 // Wrap the evaluation statement in a new function compiled in the newly
7526 // created context. The function has one parameter which has to be called
7527 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007528 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007529 // function(arguments,__source__) {return eval(__source__);}
7530 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007531 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007532 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007533 Handle<String> function_source =
7534 Factory::NewStringFromAscii(Vector<const char>(source_str,
7535 source_str_length));
7536 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007537 Compiler::CompileEval(function_source,
7538 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007539 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007540 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007541 if (boilerplate.is_null()) return Failure::Exception();
7542 Handle<JSFunction> compiled_function =
7543 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7544
7545 // Invoke the result of the compilation to get the evaluation function.
7546 bool has_pending_exception;
7547 Handle<Object> receiver(frame->receiver());
7548 Handle<Object> evaluation_function =
7549 Execution::Call(compiled_function, receiver, 0, NULL,
7550 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007551 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007552
7553 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7554 function_context);
7555
7556 // Invoke the evaluation function and return the result.
7557 const int argc = 2;
7558 Object** argv[argc] = { arguments.location(),
7559 Handle<Object>::cast(source).location() };
7560 Handle<Object> result =
7561 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7562 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007563 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007564
7565 // Skip the global proxy as it has no properties and always delegates to the
7566 // real global object.
7567 if (result->IsJSGlobalProxy()) {
7568 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7569 }
7570
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007571 return *result;
7572}
7573
7574
7575static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7576 HandleScope scope;
7577
7578 // Check the execution state and decode arguments frame and source to be
7579 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007580 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007581 Object* check_result = Runtime_CheckExecutionState(args);
7582 if (check_result->IsFailure()) return check_result;
7583 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007584 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7585
7586 // Handle the processing of break.
7587 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007588
7589 // Enter the top context from before the debugger was invoked.
7590 SaveContext save;
7591 SaveContext* top = &save;
7592 while (top != NULL && *top->context() == *Debug::debug_context()) {
7593 top = top->prev();
7594 }
7595 if (top != NULL) {
7596 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007597 }
7598
7599 // Get the global context now set to the top context from before the
7600 // debugger was invoked.
7601 Handle<Context> context = Top::global_context();
7602
7603 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007604 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007605 Handle<JSFunction>(Compiler::CompileEval(source,
7606 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007607 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007608 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007609 if (boilerplate.is_null()) return Failure::Exception();
7610 Handle<JSFunction> compiled_function =
7611 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7612 context));
7613
7614 // Invoke the result of the compilation to get the evaluation function.
7615 bool has_pending_exception;
7616 Handle<Object> receiver = Top::global();
7617 Handle<Object> result =
7618 Execution::Call(compiled_function, receiver, 0, NULL,
7619 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007620 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007621 return *result;
7622}
7623
7624
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007625static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7626 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007627 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007628
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007629 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007630 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007631
7632 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007633 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007634 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7635 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7636 // because using
7637 // instances->set(i, *GetScriptWrapper(script))
7638 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7639 // already have deferenced the instances handle.
7640 Handle<JSValue> wrapper = GetScriptWrapper(script);
7641 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007642 }
7643
7644 // Return result as a JS array.
7645 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7646 Handle<JSArray>::cast(result)->SetContent(*instances);
7647 return *result;
7648}
7649
7650
7651// Helper function used by Runtime_DebugReferencedBy below.
7652static int DebugReferencedBy(JSObject* target,
7653 Object* instance_filter, int max_references,
7654 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007655 JSFunction* arguments_function) {
7656 NoHandleAllocation ha;
7657 AssertNoAllocation no_alloc;
7658
7659 // Iterate the heap.
7660 int count = 0;
7661 JSObject* last = NULL;
7662 HeapIterator iterator;
7663 while (iterator.has_next() &&
7664 (max_references == 0 || count < max_references)) {
7665 // Only look at all JSObjects.
7666 HeapObject* heap_obj = iterator.next();
7667 if (heap_obj->IsJSObject()) {
7668 // Skip context extension objects and argument arrays as these are
7669 // checked in the context of functions using them.
7670 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007671 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007672 obj->map()->constructor() == arguments_function) {
7673 continue;
7674 }
7675
7676 // Check if the JS object has a reference to the object looked for.
7677 if (obj->ReferencesObject(target)) {
7678 // Check instance filter if supplied. This is normally used to avoid
7679 // references from mirror objects (see Runtime_IsInPrototypeChain).
7680 if (!instance_filter->IsUndefined()) {
7681 Object* V = obj;
7682 while (true) {
7683 Object* prototype = V->GetPrototype();
7684 if (prototype->IsNull()) {
7685 break;
7686 }
7687 if (instance_filter == prototype) {
7688 obj = NULL; // Don't add this object.
7689 break;
7690 }
7691 V = prototype;
7692 }
7693 }
7694
7695 if (obj != NULL) {
7696 // Valid reference found add to instance array if supplied an update
7697 // count.
7698 if (instances != NULL && count < instances_size) {
7699 instances->set(count, obj);
7700 }
7701 last = obj;
7702 count++;
7703 }
7704 }
7705 }
7706 }
7707
7708 // Check for circular reference only. This can happen when the object is only
7709 // referenced from mirrors and has a circular reference in which case the
7710 // object is not really alive and would have been garbage collected if not
7711 // referenced from the mirror.
7712 if (count == 1 && last == target) {
7713 count = 0;
7714 }
7715
7716 // Return the number of referencing objects found.
7717 return count;
7718}
7719
7720
7721// Scan the heap for objects with direct references to an object
7722// args[0]: the object to find references to
7723// args[1]: constructor function for instances to exclude (Mirror)
7724// args[2]: the the maximum number of objects to return
7725static Object* Runtime_DebugReferencedBy(Arguments args) {
7726 ASSERT(args.length() == 3);
7727
7728 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007729 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007730
7731 // Check parameters.
7732 CONVERT_CHECKED(JSObject, target, args[0]);
7733 Object* instance_filter = args[1];
7734 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7735 instance_filter->IsJSObject());
7736 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7737 RUNTIME_ASSERT(max_references >= 0);
7738
7739 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007740 JSObject* arguments_boilerplate =
7741 Top::context()->global_context()->arguments_boilerplate();
7742 JSFunction* arguments_function =
7743 JSFunction::cast(arguments_boilerplate->map()->constructor());
7744
7745 // Get the number of referencing objects.
7746 int count;
7747 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007748 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007749
7750 // Allocate an array to hold the result.
7751 Object* object = Heap::AllocateFixedArray(count);
7752 if (object->IsFailure()) return object;
7753 FixedArray* instances = FixedArray::cast(object);
7754
7755 // Fill the referencing objects.
7756 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007757 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007758
7759 // Return result as JS array.
7760 Object* result =
7761 Heap::AllocateJSObject(
7762 Top::context()->global_context()->array_function());
7763 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7764 return result;
7765}
7766
7767
7768// Helper function used by Runtime_DebugConstructedBy below.
7769static int DebugConstructedBy(JSFunction* constructor, int max_references,
7770 FixedArray* instances, int instances_size) {
7771 AssertNoAllocation no_alloc;
7772
7773 // Iterate the heap.
7774 int count = 0;
7775 HeapIterator iterator;
7776 while (iterator.has_next() &&
7777 (max_references == 0 || count < max_references)) {
7778 // Only look at all JSObjects.
7779 HeapObject* heap_obj = iterator.next();
7780 if (heap_obj->IsJSObject()) {
7781 JSObject* obj = JSObject::cast(heap_obj);
7782 if (obj->map()->constructor() == constructor) {
7783 // Valid reference found add to instance array if supplied an update
7784 // count.
7785 if (instances != NULL && count < instances_size) {
7786 instances->set(count, obj);
7787 }
7788 count++;
7789 }
7790 }
7791 }
7792
7793 // Return the number of referencing objects found.
7794 return count;
7795}
7796
7797
7798// Scan the heap for objects constructed by a specific function.
7799// args[0]: the constructor to find instances of
7800// args[1]: the the maximum number of objects to return
7801static Object* Runtime_DebugConstructedBy(Arguments args) {
7802 ASSERT(args.length() == 2);
7803
7804 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007805 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007806
7807 // Check parameters.
7808 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7809 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7810 RUNTIME_ASSERT(max_references >= 0);
7811
7812 // Get the number of referencing objects.
7813 int count;
7814 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7815
7816 // Allocate an array to hold the result.
7817 Object* object = Heap::AllocateFixedArray(count);
7818 if (object->IsFailure()) return object;
7819 FixedArray* instances = FixedArray::cast(object);
7820
7821 // Fill the referencing objects.
7822 count = DebugConstructedBy(constructor, max_references, instances, count);
7823
7824 // Return result as JS array.
7825 Object* result =
7826 Heap::AllocateJSObject(
7827 Top::context()->global_context()->array_function());
7828 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7829 return result;
7830}
7831
7832
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007833// Find the effective prototype object as returned by __proto__.
7834// args[0]: the object to find the prototype for.
7835static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007836 ASSERT(args.length() == 1);
7837
7838 CONVERT_CHECKED(JSObject, obj, args[0]);
7839
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007840 // Use the __proto__ accessor.
7841 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007842}
7843
7844
7845static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007846 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007847 CPU::DebugBreak();
7848 return Heap::undefined_value();
7849}
7850
7851
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007852static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007853#ifdef DEBUG
7854 HandleScope scope;
7855 ASSERT(args.length() == 1);
7856 // Get the function and make sure it is compiled.
7857 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7858 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7859 return Failure::Exception();
7860 }
7861 func->code()->PrintLn();
7862#endif // DEBUG
7863 return Heap::undefined_value();
7864}
ager@chromium.org9085a012009-05-11 19:22:57 +00007865
7866
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007867static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7868#ifdef DEBUG
7869 HandleScope scope;
7870 ASSERT(args.length() == 1);
7871 // Get the function and make sure it is compiled.
7872 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7873 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7874 return Failure::Exception();
7875 }
7876 func->shared()->construct_stub()->PrintLn();
7877#endif // DEBUG
7878 return Heap::undefined_value();
7879}
7880
7881
ager@chromium.org9085a012009-05-11 19:22:57 +00007882static Object* Runtime_FunctionGetInferredName(Arguments args) {
7883 NoHandleAllocation ha;
7884 ASSERT(args.length() == 1);
7885
7886 CONVERT_CHECKED(JSFunction, f, args[0]);
7887 return f->shared()->inferred_name();
7888}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007889
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007890#endif // ENABLE_DEBUGGER_SUPPORT
7891
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007892#ifdef ENABLE_LOGGING_AND_PROFILING
7893
7894static Object* Runtime_ProfilerResume(Arguments args) {
7895 NoHandleAllocation ha;
7896 ASSERT(args.length() == 1);
7897
7898 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7899 v8::V8::ResumeProfilerEx(smi_modules->value());
7900 return Heap::undefined_value();
7901}
7902
7903
7904static Object* Runtime_ProfilerPause(Arguments args) {
7905 NoHandleAllocation ha;
7906 ASSERT(args.length() == 1);
7907
7908 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7909 v8::V8::PauseProfilerEx(smi_modules->value());
7910 return Heap::undefined_value();
7911}
7912
7913#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007914
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007915// Finds the script object from the script data. NOTE: This operation uses
7916// heap traversal to find the function generated for the source position
7917// for the requested break point. For lazily compiled functions several heap
7918// traversals might be required rendering this operation as a rather slow
7919// operation. However for setting break points which is normally done through
7920// some kind of user interaction the performance is not crucial.
7921static Handle<Object> Runtime_GetScriptFromScriptName(
7922 Handle<String> script_name) {
7923 // Scan the heap for Script objects to find the script with the requested
7924 // script data.
7925 Handle<Script> script;
7926 HeapIterator iterator;
7927 while (script.is_null() && iterator.has_next()) {
7928 HeapObject* obj = iterator.next();
7929 // If a script is found check if it has the script data requested.
7930 if (obj->IsScript()) {
7931 if (Script::cast(obj)->name()->IsString()) {
7932 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7933 script = Handle<Script>(Script::cast(obj));
7934 }
7935 }
7936 }
7937 }
7938
7939 // If no script with the requested script data is found return undefined.
7940 if (script.is_null()) return Factory::undefined_value();
7941
7942 // Return the script found.
7943 return GetScriptWrapper(script);
7944}
7945
7946
7947// Get the script object from script data. NOTE: Regarding performance
7948// see the NOTE for GetScriptFromScriptData.
7949// args[0]: script data for the script to find the source for
7950static Object* Runtime_GetScript(Arguments args) {
7951 HandleScope scope;
7952
7953 ASSERT(args.length() == 1);
7954
7955 CONVERT_CHECKED(String, script_name, args[0]);
7956
7957 // Find the requested script.
7958 Handle<Object> result =
7959 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7960 return *result;
7961}
7962
7963
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007964// Determines whether the given stack frame should be displayed in
7965// a stack trace. The caller is the error constructor that asked
7966// for the stack trace to be collected. The first time a construct
7967// call to this function is encountered it is skipped. The seen_caller
7968// in/out parameter is used to remember if the caller has been seen
7969// yet.
7970static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7971 bool* seen_caller) {
7972 // Only display JS frames.
7973 if (!raw_frame->is_java_script())
7974 return false;
7975 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7976 Object* raw_fun = frame->function();
7977 // Not sure when this can happen but skip it just in case.
7978 if (!raw_fun->IsJSFunction())
7979 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007980 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007981 *seen_caller = true;
7982 return false;
7983 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007984 // Skip all frames until we've seen the caller. Also, skip the most
7985 // obvious builtin calls. Some builtin calls (such as Number.ADD
7986 // which is invoked using 'call') are very difficult to recognize
7987 // so we're leaving them in for now.
7988 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007989}
7990
7991
7992// Collect the raw data for a stack trace. Returns an array of three
7993// element segments each containing a receiver, function and native
7994// code offset.
7995static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007996 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007997 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007998 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7999
8000 HandleScope scope;
8001
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008002 limit = Max(limit, 0); // Ensure that limit is not negative.
8003 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008004 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008005
8006 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008007 // If the caller parameter is a function we skip frames until we're
8008 // under it before starting to collect.
8009 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008010 int cursor = 0;
8011 int frames_seen = 0;
8012 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008013 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008014 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008015 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008016 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008017 Object* recv = frame->receiver();
8018 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008019 Address pc = frame->pc();
8020 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008021 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008022 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008023 if (cursor + 2 < elements->length()) {
8024 elements->set(cursor++, recv);
8025 elements->set(cursor++, fun);
8026 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
8027 } else {
8028 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008029 Handle<Object> recv_handle(recv);
8030 Handle<Object> fun_handle(fun);
8031 SetElement(result, cursor++, recv_handle);
8032 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008033 SetElement(result, cursor++, Handle<Smi>(offset));
8034 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008035 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008036 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008037 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008038
8039 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
8040
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008041 return *result;
8042}
8043
8044
ager@chromium.org3811b432009-10-28 14:53:37 +00008045// Returns V8 version as a string.
8046static Object* Runtime_GetV8Version(Arguments args) {
8047 ASSERT_EQ(args.length(), 0);
8048
8049 NoHandleAllocation ha;
8050
8051 const char* version_string = v8::V8::GetVersion();
8052
8053 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
8054}
8055
8056
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008057static Object* Runtime_Abort(Arguments args) {
8058 ASSERT(args.length() == 2);
8059 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
8060 Smi::cast(args[1])->value());
8061 Top::PrintStack();
8062 OS::Abort();
8063 UNREACHABLE();
8064 return NULL;
8065}
8066
8067
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008068static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
8069 ASSERT(args.length() == 0);
8070 HandleScope::DeleteExtensions();
8071 return Heap::undefined_value();
8072}
8073
8074
kasper.lund44510672008-07-25 07:37:58 +00008075#ifdef DEBUG
8076// ListNatives is ONLY used by the fuzz-natives.js in debug mode
8077// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008078static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008079 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008080 HandleScope scope;
8081 Handle<JSArray> result = Factory::NewJSArray(0);
8082 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00008083#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008084 { \
8085 HandleScope inner; \
8086 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008087 Factory::NewStringFromAscii( \
8088 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008089 Handle<JSArray> pair = Factory::NewJSArray(0); \
8090 SetElement(pair, 0, name); \
8091 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
8092 SetElement(result, index++, pair); \
8093 }
8094 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
8095#undef ADD_ENTRY
8096 return *result;
8097}
kasper.lund44510672008-07-25 07:37:58 +00008098#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008099
8100
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008101static Object* Runtime_Log(Arguments args) {
8102 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00008103 CONVERT_CHECKED(String, format, args[0]);
8104 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008105 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008106 Logger::LogRuntime(chars, elms);
8107 return Heap::undefined_value();
8108}
8109
8110
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008111static Object* Runtime_IS_VAR(Arguments args) {
8112 UNREACHABLE(); // implemented as macro in the parser
8113 return NULL;
8114}
8115
8116
8117// ----------------------------------------------------------------------------
8118// Implementation of Runtime
8119
ager@chromium.orga1645e22009-09-09 19:27:10 +00008120#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008121 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00008122 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008123
8124static Runtime::Function Runtime_functions[] = {
8125 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00008126 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008127};
8128
8129#undef F
8130
8131
8132Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
8133 ASSERT(0 <= fid && fid < kNofFunctions);
8134 return &Runtime_functions[fid];
8135}
8136
8137
8138Runtime::Function* Runtime::FunctionForName(const char* name) {
8139 for (Function* f = Runtime_functions; f->name != NULL; f++) {
8140 if (strcmp(f->name, name) == 0) {
8141 return f;
8142 }
8143 }
8144 return NULL;
8145}
8146
8147
8148void Runtime::PerformGC(Object* result) {
8149 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008150 if (failure->IsRetryAfterGC()) {
8151 // Try to do a garbage collection; ignore it if it fails. The C
8152 // entry stub will throw an out-of-memory exception in that case.
8153 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
8154 } else {
8155 // Handle last resort GC and make sure to allow future allocations
8156 // to grow the heap without causing GCs (if possible).
8157 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008158 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008159 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008160}
8161
8162
8163} } // namespace v8::internal