blob: 834983344d9597cf0cef4563bc590642d964b072 [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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000562static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000563 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000564 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000565 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
566 CONVERT_ARG_CHECKED(String, pattern, 1);
567 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000568 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
569 if (result.is_null()) return Failure::Exception();
570 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000571}
572
573
574static Object* Runtime_CreateApiFunction(Arguments args) {
575 HandleScope scope;
576 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000577 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578 return *Factory::CreateApiFunction(data);
579}
580
581
582static Object* Runtime_IsTemplate(Arguments args) {
583 ASSERT(args.length() == 1);
584 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000585 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000586 return Heap::ToBoolean(result);
587}
588
589
590static Object* Runtime_GetTemplateField(Arguments args) {
591 ASSERT(args.length() == 2);
592 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000593 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000594 int index = field->value();
595 int offset = index * kPointerSize + HeapObject::kHeaderSize;
596 InstanceType type = templ->map()->instance_type();
597 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
598 type == OBJECT_TEMPLATE_INFO_TYPE);
599 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000600 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000601 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
602 } else {
603 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
604 }
605 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000606}
607
608
ager@chromium.org870a0b62008-11-04 11:43:05 +0000609static Object* Runtime_DisableAccessChecks(Arguments args) {
610 ASSERT(args.length() == 1);
611 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000612 Map* old_map = object->map();
613 bool needs_access_checks = old_map->is_access_check_needed();
614 if (needs_access_checks) {
615 // Copy map so it won't interfere constructor's initial map.
616 Object* new_map = old_map->CopyDropTransitions();
617 if (new_map->IsFailure()) return new_map;
618
619 Map::cast(new_map)->set_is_access_check_needed(false);
620 object->set_map(Map::cast(new_map));
621 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000622 return needs_access_checks ? Heap::true_value() : Heap::false_value();
623}
624
625
626static Object* Runtime_EnableAccessChecks(Arguments args) {
627 ASSERT(args.length() == 1);
628 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000629 Map* old_map = object->map();
630 if (!old_map->is_access_check_needed()) {
631 // Copy map so it won't interfere constructor's initial map.
632 Object* new_map = old_map->CopyDropTransitions();
633 if (new_map->IsFailure()) return new_map;
634
635 Map::cast(new_map)->set_is_access_check_needed(true);
636 object->set_map(Map::cast(new_map));
637 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000638 return Heap::undefined_value();
639}
640
641
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000642static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
643 HandleScope scope;
644 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
645 Handle<Object> args[2] = { type_handle, name };
646 Handle<Object> error =
647 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
648 return Top::Throw(*error);
649}
650
651
652static Object* Runtime_DeclareGlobals(Arguments args) {
653 HandleScope scope;
654 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
655
ager@chromium.org3811b432009-10-28 14:53:37 +0000656 Handle<Context> context = args.at<Context>(0);
657 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000658 bool is_eval = Smi::cast(args[2])->value() == 1;
659
660 // Compute the property attributes. According to ECMA-262, section
661 // 13, page 71, the property must be read-only and
662 // non-deletable. However, neither SpiderMonkey nor KJS creates the
663 // property as read-only, so we don't either.
664 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
665
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000666 // Traverse the name/value pairs and set the properties.
667 int length = pairs->length();
668 for (int i = 0; i < length; i += 2) {
669 HandleScope scope;
670 Handle<String> name(String::cast(pairs->get(i)));
671 Handle<Object> value(pairs->get(i + 1));
672
673 // We have to declare a global const property. To capture we only
674 // assign to it when evaluating the assignment for "const x =
675 // <expr>" the initial value is the hole.
676 bool is_const_property = value->IsTheHole();
677
678 if (value->IsUndefined() || is_const_property) {
679 // Lookup the property in the global object, and don't set the
680 // value of the variable if the property is already there.
681 LookupResult lookup;
682 global->Lookup(*name, &lookup);
683 if (lookup.IsProperty()) {
684 // Determine if the property is local by comparing the holder
685 // against the global object. The information will be used to
686 // avoid throwing re-declaration errors when declaring
687 // variables or constants that exist in the prototype chain.
688 bool is_local = (*global == lookup.holder());
689 // Get the property attributes and determine if the property is
690 // read-only.
691 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
692 bool is_read_only = (attributes & READ_ONLY) != 0;
693 if (lookup.type() == INTERCEPTOR) {
694 // If the interceptor says the property is there, we
695 // just return undefined without overwriting the property.
696 // Otherwise, we continue to setting the property.
697 if (attributes != ABSENT) {
698 // Check if the existing property conflicts with regards to const.
699 if (is_local && (is_read_only || is_const_property)) {
700 const char* type = (is_read_only) ? "const" : "var";
701 return ThrowRedeclarationError(type, name);
702 };
703 // The property already exists without conflicting: Go to
704 // the next declaration.
705 continue;
706 }
707 // Fall-through and introduce the absent property by using
708 // SetProperty.
709 } else {
710 if (is_local && (is_read_only || is_const_property)) {
711 const char* type = (is_read_only) ? "const" : "var";
712 return ThrowRedeclarationError(type, name);
713 }
714 // The property already exists without conflicting: Go to
715 // the next declaration.
716 continue;
717 }
718 }
719 } else {
720 // Copy the function and update its context. Use it as value.
721 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
722 Handle<JSFunction> function =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +0000723 Factory::NewFunctionFromBoilerplate(boilerplate, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724 value = function;
725 }
726
727 LookupResult lookup;
728 global->LocalLookup(*name, &lookup);
729
730 PropertyAttributes attributes = is_const_property
731 ? static_cast<PropertyAttributes>(base | READ_ONLY)
732 : base;
733
734 if (lookup.IsProperty()) {
735 // There's a local property that we need to overwrite because
736 // we're either declaring a function or there's an interceptor
737 // that claims the property is absent.
738
739 // Check for conflicting re-declarations. We cannot have
740 // conflicting types in case of intercepted properties because
741 // they are absent.
742 if (lookup.type() != INTERCEPTOR &&
743 (lookup.IsReadOnly() || is_const_property)) {
744 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
745 return ThrowRedeclarationError(type, name);
746 }
747 SetProperty(global, name, value, attributes);
748 } else {
749 // If a property with this name does not already exist on the
750 // global object add the property locally. We take special
751 // precautions to always add it as a local property even in case
752 // of callbacks in the prototype chain (this rules out using
753 // SetProperty). Also, we must use the handle-based version to
754 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000755 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000756 }
757 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000758
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000759 return Heap::undefined_value();
760}
761
762
763static Object* Runtime_DeclareContextSlot(Arguments args) {
764 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000765 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766
ager@chromium.org7c537e22008-10-16 08:43:32 +0000767 CONVERT_ARG_CHECKED(Context, context, 0);
768 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000770 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000771 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000772 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000773
774 // Declarations are always done in the function context.
775 context = Handle<Context>(context->fcontext());
776
777 int index;
778 PropertyAttributes attributes;
779 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000780 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000781 context->Lookup(name, flags, &index, &attributes);
782
783 if (attributes != ABSENT) {
784 // The name was declared before; check for conflicting
785 // re-declarations: This is similar to the code in parser.cc in
786 // the AstBuildingParser::Declare function.
787 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
788 // Functions are not read-only.
789 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
790 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
791 return ThrowRedeclarationError(type, name);
792 }
793
794 // Initialize it if necessary.
795 if (*initial_value != NULL) {
796 if (index >= 0) {
797 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000798 // the function context or the arguments object.
799 if (holder->IsContext()) {
800 ASSERT(holder.is_identical_to(context));
801 if (((attributes & READ_ONLY) == 0) ||
802 context->get(index)->IsTheHole()) {
803 context->set(index, *initial_value);
804 }
805 } else {
806 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000807 }
808 } else {
809 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000810 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000811 SetProperty(context_ext, name, initial_value, mode);
812 }
813 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000815 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000816 // The property is not in the function context. It needs to be
817 // "declared" in the function context's extension context, or in the
818 // global context.
819 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000820 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000821 // The function context's extension context exists - use it.
822 context_ext = Handle<JSObject>(context->extension());
823 } else {
824 // The function context's extension context does not exists - allocate
825 // it.
826 context_ext = Factory::NewJSObject(Top::context_extension_function());
827 // And store it in the extension slot.
828 context->set_extension(*context_ext);
829 }
830 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000831
ager@chromium.org7c537e22008-10-16 08:43:32 +0000832 // Declare the property by setting it to the initial value if provided,
833 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
834 // constant declarations).
835 ASSERT(!context_ext->HasLocalProperty(*name));
836 Handle<Object> value(Heap::undefined_value());
837 if (*initial_value != NULL) value = initial_value;
838 SetProperty(context_ext, name, value, mode);
839 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
840 }
841
842 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000843}
844
845
846static Object* Runtime_InitializeVarGlobal(Arguments args) {
847 NoHandleAllocation nha;
848
849 // Determine if we need to assign to the variable if it already
850 // exists (based on the number of arguments).
851 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
852 bool assign = args.length() == 2;
853
854 CONVERT_ARG_CHECKED(String, name, 0);
855 GlobalObject* global = Top::context()->global();
856
857 // According to ECMA-262, section 12.2, page 62, the property must
858 // not be deletable.
859 PropertyAttributes attributes = DONT_DELETE;
860
861 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000862 // there, there is a property with this name in the prototype chain.
863 // We follow Safari and Firefox behavior and only set the property
864 // locally if there is an explicit initialization value that we have
865 // to assign to the property. When adding the property we take
866 // special precautions to always add it as a local property even in
867 // case of callbacks in the prototype chain (this rules out using
868 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
869 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000870 // Note that objects can have hidden prototypes, so we need to traverse
871 // the whole chain of hidden prototypes to do a 'local' lookup.
872 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000873 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000874 while (true) {
875 real_holder->LocalLookup(*name, &lookup);
876 if (lookup.IsProperty()) {
877 // Determine if this is a redeclaration of something read-only.
878 if (lookup.IsReadOnly()) {
879 // If we found readonly property on one of hidden prototypes,
880 // just shadow it.
881 if (real_holder != Top::context()->global()) break;
882 return ThrowRedeclarationError("const", name);
883 }
884
885 // Determine if this is a redeclaration of an intercepted read-only
886 // property and figure out if the property exists at all.
887 bool found = true;
888 PropertyType type = lookup.type();
889 if (type == INTERCEPTOR) {
890 HandleScope handle_scope;
891 Handle<JSObject> holder(real_holder);
892 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
893 real_holder = *holder;
894 if (intercepted == ABSENT) {
895 // The interceptor claims the property isn't there. We need to
896 // make sure to introduce it.
897 found = false;
898 } else if ((intercepted & READ_ONLY) != 0) {
899 // The property is present, but read-only. Since we're trying to
900 // overwrite it with a variable declaration we must throw a
901 // re-declaration error. However if we found readonly property
902 // on one of hidden prototypes, just shadow it.
903 if (real_holder != Top::context()->global()) break;
904 return ThrowRedeclarationError("const", name);
905 }
906 }
907
908 if (found && !assign) {
909 // The global property is there and we're not assigning any value
910 // to it. Just return.
911 return Heap::undefined_value();
912 }
913
914 // Assign the value (or undefined) to the property.
915 Object* value = (assign) ? args[1] : Heap::undefined_value();
916 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000917 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000918
919 Object* proto = real_holder->GetPrototype();
920 if (!proto->IsJSObject())
921 break;
922
923 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
924 break;
925
926 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000927 }
928
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000929 global = Top::context()->global();
930 if (assign) {
931 return global->IgnoreAttributesAndSetLocalProperty(*name,
932 args[1],
933 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000934 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000935 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000936}
937
938
939static Object* Runtime_InitializeConstGlobal(Arguments args) {
940 // All constants are declared with an initial value. The name
941 // of the constant is the first argument and the initial value
942 // is the second.
943 RUNTIME_ASSERT(args.length() == 2);
944 CONVERT_ARG_CHECKED(String, name, 0);
945 Handle<Object> value = args.at<Object>(1);
946
947 // Get the current global object from top.
948 GlobalObject* global = Top::context()->global();
949
950 // According to ECMA-262, section 12.2, page 62, the property must
951 // not be deletable. Since it's a const, it must be READ_ONLY too.
952 PropertyAttributes attributes =
953 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
954
955 // Lookup the property locally in the global object. If it isn't
956 // there, we add the property and take special precautions to always
957 // add it as a local property even in case of callbacks in the
958 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000959 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000960 LookupResult lookup;
961 global->LocalLookup(*name, &lookup);
962 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000963 return global->IgnoreAttributesAndSetLocalProperty(*name,
964 *value,
965 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000966 }
967
968 // Determine if this is a redeclaration of something not
969 // read-only. In case the result is hidden behind an interceptor we
970 // need to ask it for the property attributes.
971 if (!lookup.IsReadOnly()) {
972 if (lookup.type() != INTERCEPTOR) {
973 return ThrowRedeclarationError("var", name);
974 }
975
976 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
977
978 // Throw re-declaration error if the intercepted property is present
979 // but not read-only.
980 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
981 return ThrowRedeclarationError("var", name);
982 }
983
984 // Restore global object from context (in case of GC) and continue
985 // with setting the value because the property is either absent or
986 // read-only. We also have to do redo the lookup.
987 global = Top::context()->global();
988
989 // BUG 1213579: Handle the case where we have to set a read-only
990 // property through an interceptor and only do it if it's
991 // uninitialized, e.g. the hole. Nirk...
992 global->SetProperty(*name, *value, attributes);
993 return *value;
994 }
995
996 // Set the value, but only we're assigning the initial value to a
997 // constant. For now, we determine this by checking if the
998 // current value is the hole.
999 PropertyType type = lookup.type();
1000 if (type == FIELD) {
1001 FixedArray* properties = global->properties();
1002 int index = lookup.GetFieldIndex();
1003 if (properties->get(index)->IsTheHole()) {
1004 properties->set(index, *value);
1005 }
1006 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001007 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1008 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001009 }
1010 } else {
1011 // Ignore re-initialization of constants that have already been
1012 // assigned a function value.
1013 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1014 }
1015
1016 // Use the set value as the result of the operation.
1017 return *value;
1018}
1019
1020
1021static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1022 HandleScope scope;
1023 ASSERT(args.length() == 3);
1024
1025 Handle<Object> value(args[0]);
1026 ASSERT(!value->IsTheHole());
1027 CONVERT_ARG_CHECKED(Context, context, 1);
1028 Handle<String> name(String::cast(args[2]));
1029
1030 // Initializations are always done in the function context.
1031 context = Handle<Context>(context->fcontext());
1032
1033 int index;
1034 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001035 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001036 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 context->Lookup(name, flags, &index, &attributes);
1038
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001039 // In most situations, the property introduced by the const
1040 // declaration should be present in the context extension object.
1041 // However, because declaration and initialization are separate, the
1042 // property might have been deleted (if it was introduced by eval)
1043 // before we reach the initialization point.
1044 //
1045 // Example:
1046 //
1047 // function f() { eval("delete x; const x;"); }
1048 //
1049 // In that case, the initialization behaves like a normal assignment
1050 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001051 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001052 // Property was found in a context.
1053 if (holder->IsContext()) {
1054 // The holder cannot be the function context. If it is, there
1055 // should have been a const redeclaration error when declaring
1056 // the const property.
1057 ASSERT(!holder.is_identical_to(context));
1058 if ((attributes & READ_ONLY) == 0) {
1059 Handle<Context>::cast(holder)->set(index, *value);
1060 }
1061 } else {
1062 // The holder is an arguments object.
1063 ASSERT((attributes & READ_ONLY) == 0);
1064 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001065 }
1066 return *value;
1067 }
1068
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001069 // The property could not be found, we introduce it in the global
1070 // context.
1071 if (attributes == ABSENT) {
1072 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1073 SetProperty(global, name, value, NONE);
1074 return *value;
1075 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001076
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001077 // The property was present in a context extension object.
1078 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001079
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001080 if (*context_ext == context->extension()) {
1081 // This is the property that was introduced by the const
1082 // declaration. Set it if it hasn't been set before. NOTE: We
1083 // cannot use GetProperty() to get the current value as it
1084 // 'unholes' the value.
1085 LookupResult lookup;
1086 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1087 ASSERT(lookup.IsProperty()); // the property was declared
1088 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1089
1090 PropertyType type = lookup.type();
1091 if (type == FIELD) {
1092 FixedArray* properties = context_ext->properties();
1093 int index = lookup.GetFieldIndex();
1094 if (properties->get(index)->IsTheHole()) {
1095 properties->set(index, *value);
1096 }
1097 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001098 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1099 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001100 }
1101 } else {
1102 // We should not reach here. Any real, named property should be
1103 // either a field or a dictionary slot.
1104 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001105 }
1106 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001107 // The property was found in a different context extension object.
1108 // Set it if it is not a read-only property.
1109 if ((attributes & READ_ONLY) == 0) {
1110 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1111 // Setting a property might throw an exception. Exceptions
1112 // are converted to empty handles in handle operations. We
1113 // need to convert back to exceptions here.
1114 if (set.is_null()) {
1115 ASSERT(Top::has_pending_exception());
1116 return Failure::Exception();
1117 }
1118 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001119 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001120
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001121 return *value;
1122}
1123
1124
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001125static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1126 Arguments args) {
1127 HandleScope scope;
1128 ASSERT(args.length() == 2);
1129 CONVERT_ARG_CHECKED(JSObject, object, 0);
1130 CONVERT_SMI_CHECKED(properties, args[1]);
1131 if (object->HasFastProperties()) {
1132 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1133 }
1134 return *object;
1135}
1136
1137
1138static Object* Runtime_TransformToFastProperties(Arguments args) {
1139 HandleScope scope;
1140 ASSERT(args.length() == 1);
1141 CONVERT_ARG_CHECKED(JSObject, object, 0);
1142 if (!object->HasFastProperties() && !object->IsGlobalObject()) {
1143 TransformToFastProperties(object, 0);
1144 }
1145 return *object;
1146}
1147
1148
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001149static Object* Runtime_RegExpExec(Arguments args) {
1150 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001151 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001152 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1153 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001154 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001155 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001156 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001157 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001158 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001159 RUNTIME_ASSERT(index >= 0);
1160 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001161 Handle<Object> result = RegExpImpl::Exec(regexp,
1162 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001163 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001164 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001165 if (result.is_null()) return Failure::Exception();
1166 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001167}
1168
1169
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001170static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1171 HandleScope scope;
1172 ASSERT(args.length() == 4);
1173 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1174 int index = Smi::cast(args[1])->value();
1175 Handle<String> pattern = args.at<String>(2);
1176 Handle<String> flags = args.at<String>(3);
1177
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001178 // Get the RegExp function from the context in the literals array.
1179 // This is the RegExp function from the context in which the
1180 // function was created. We do not use the RegExp function from the
1181 // current global context because this might be the RegExp function
1182 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001183 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001184 Handle<JSFunction>(
1185 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001186 // Compute the regular expression literal.
1187 bool has_pending_exception;
1188 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001189 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1190 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001191 if (has_pending_exception) {
1192 ASSERT(Top::has_pending_exception());
1193 return Failure::Exception();
1194 }
1195 literals->set(index, *regexp);
1196 return *regexp;
1197}
1198
1199
1200static Object* Runtime_FunctionGetName(Arguments args) {
1201 NoHandleAllocation ha;
1202 ASSERT(args.length() == 1);
1203
1204 CONVERT_CHECKED(JSFunction, f, args[0]);
1205 return f->shared()->name();
1206}
1207
1208
ager@chromium.org236ad962008-09-25 09:45:57 +00001209static Object* Runtime_FunctionSetName(Arguments args) {
1210 NoHandleAllocation ha;
1211 ASSERT(args.length() == 2);
1212
1213 CONVERT_CHECKED(JSFunction, f, args[0]);
1214 CONVERT_CHECKED(String, name, args[1]);
1215 f->shared()->set_name(name);
1216 return Heap::undefined_value();
1217}
1218
1219
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001220static Object* Runtime_FunctionGetScript(Arguments args) {
1221 HandleScope scope;
1222 ASSERT(args.length() == 1);
1223
1224 CONVERT_CHECKED(JSFunction, fun, args[0]);
1225 Handle<Object> script = Handle<Object>(fun->shared()->script());
1226 if (!script->IsScript()) return Heap::undefined_value();
1227
1228 return *GetScriptWrapper(Handle<Script>::cast(script));
1229}
1230
1231
1232static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1233 NoHandleAllocation ha;
1234 ASSERT(args.length() == 1);
1235
1236 CONVERT_CHECKED(JSFunction, f, args[0]);
1237 return f->shared()->GetSourceCode();
1238}
1239
1240
1241static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1242 NoHandleAllocation ha;
1243 ASSERT(args.length() == 1);
1244
1245 CONVERT_CHECKED(JSFunction, fun, args[0]);
1246 int pos = fun->shared()->start_position();
1247 return Smi::FromInt(pos);
1248}
1249
1250
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001251static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1252 ASSERT(args.length() == 2);
1253
1254 CONVERT_CHECKED(JSFunction, fun, args[0]);
1255 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1256
1257 Code* code = fun->code();
1258 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1259
1260 Address pc = code->address() + offset;
1261 return Smi::FromInt(fun->code()->SourcePosition(pc));
1262}
1263
1264
1265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001266static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1267 NoHandleAllocation ha;
1268 ASSERT(args.length() == 2);
1269
1270 CONVERT_CHECKED(JSFunction, fun, args[0]);
1271 CONVERT_CHECKED(String, name, args[1]);
1272 fun->SetInstanceClassName(name);
1273 return Heap::undefined_value();
1274}
1275
1276
1277static Object* Runtime_FunctionSetLength(Arguments args) {
1278 NoHandleAllocation ha;
1279 ASSERT(args.length() == 2);
1280
1281 CONVERT_CHECKED(JSFunction, fun, args[0]);
1282 CONVERT_CHECKED(Smi, length, args[1]);
1283 fun->shared()->set_length(length->value());
1284 return length;
1285}
1286
1287
1288static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001289 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001290 ASSERT(args.length() == 2);
1291
1292 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001293 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1294 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001295 return args[0]; // return TOS
1296}
1297
1298
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001299static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1300 NoHandleAllocation ha;
1301 ASSERT(args.length() == 1);
1302
1303 CONVERT_CHECKED(JSFunction, f, args[0]);
1304 // The function_data field of the shared function info is used exclusively by
1305 // the API.
1306 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1307 : Heap::false_value();
1308}
1309
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001310static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1311 NoHandleAllocation ha;
1312 ASSERT(args.length() == 1);
1313
1314 CONVERT_CHECKED(JSFunction, f, args[0]);
1315 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1316}
1317
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001318
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001319static Object* Runtime_SetCode(Arguments args) {
1320 HandleScope scope;
1321 ASSERT(args.length() == 2);
1322
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001323 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001324 Handle<Object> code = args.at<Object>(1);
1325
1326 Handle<Context> context(target->context());
1327
1328 if (!code->IsNull()) {
1329 RUNTIME_ASSERT(code->IsJSFunction());
1330 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1331 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1332 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1333 return Failure::Exception();
1334 }
1335 // Set the code, formal parameter count, and the length of the target
1336 // function.
1337 target->set_code(fun->code());
1338 target->shared()->set_length(fun->shared()->length());
1339 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001340 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001341 // Set the source code of the target function to undefined.
1342 // SetCode is only used for built-in constructors like String,
1343 // Array, and Object, and some web code
1344 // doesn't like seeing source code for constructors.
1345 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001346 // Clear the optimization hints related to the compiled code as these are no
1347 // longer valid when the code is overwritten.
1348 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001349 context = Handle<Context>(fun->context());
1350
1351 // Make sure we get a fresh copy of the literal vector to avoid
1352 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001353 int number_of_literals = fun->NumberOfLiterals();
1354 Handle<FixedArray> literals =
1355 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001356 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001357 // Insert the object, regexp and array functions in the literals
1358 // array prefix. These are the functions that will be used when
1359 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001360 literals->set(JSFunction::kLiteralGlobalContextIndex,
1361 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001362 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001363 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001364 }
1365
1366 target->set_context(*context);
1367 return *target;
1368}
1369
1370
1371static Object* CharCodeAt(String* subject, Object* index) {
1372 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001373 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001374 // Flatten the string. If someone wants to get a char at an index
1375 // in a cons string, it is likely that more indices will be
1376 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001377 Object* flat = subject->TryFlatten();
1378 if (flat->IsFailure()) return flat;
1379 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001380 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001381 return Heap::nan_value();
1382 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001383 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001384}
1385
1386
1387static Object* Runtime_StringCharCodeAt(Arguments args) {
1388 NoHandleAllocation ha;
1389 ASSERT(args.length() == 2);
1390
1391 CONVERT_CHECKED(String, subject, args[0]);
1392 Object* index = args[1];
1393 return CharCodeAt(subject, index);
1394}
1395
1396
1397static Object* Runtime_CharFromCode(Arguments args) {
1398 NoHandleAllocation ha;
1399 ASSERT(args.length() == 1);
1400 uint32_t code;
1401 if (Array::IndexFromObject(args[0], &code)) {
1402 if (code <= 0xffff) {
1403 return Heap::LookupSingleCharacterStringFromCode(code);
1404 }
1405 }
1406 return Heap::empty_string();
1407}
1408
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001409// Forward declarations.
1410static const int kStringBuilderConcatHelperLengthBits = 11;
1411static const int kStringBuilderConcatHelperPositionBits = 19;
1412
1413template <typename schar>
1414static inline void StringBuilderConcatHelper(String*,
1415 schar*,
1416 FixedArray*,
1417 int);
1418
1419typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1420typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1421
1422class ReplacementStringBuilder {
1423 public:
1424 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1425 : subject_(subject),
1426 parts_(Factory::NewFixedArray(estimated_part_count)),
1427 part_count_(0),
1428 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001429 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001430 // Require a non-zero initial size. Ensures that doubling the size to
1431 // extend the array will work.
1432 ASSERT(estimated_part_count > 0);
1433 }
1434
1435 void EnsureCapacity(int elements) {
1436 int length = parts_->length();
1437 int required_length = part_count_ + elements;
1438 if (length < required_length) {
1439 int new_length = length;
1440 do {
1441 new_length *= 2;
1442 } while (new_length < required_length);
1443 Handle<FixedArray> extended_array =
1444 Factory::NewFixedArray(new_length);
1445 parts_->CopyTo(0, *extended_array, 0, part_count_);
1446 parts_ = extended_array;
1447 }
1448 }
1449
1450 void AddSubjectSlice(int from, int to) {
1451 ASSERT(from >= 0);
1452 int length = to - from;
1453 ASSERT(length > 0);
1454 // Can we encode the slice in 11 bits for length and 19 bits for
1455 // start position - as used by StringBuilderConcatHelper?
1456 if (StringBuilderSubstringLength::is_valid(length) &&
1457 StringBuilderSubstringPosition::is_valid(from)) {
1458 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1459 StringBuilderSubstringPosition::encode(from);
1460 AddElement(Smi::FromInt(encoded_slice));
1461 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001462 // Otherwise encode as two smis.
1463 AddElement(Smi::FromInt(-length));
1464 AddElement(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001465 }
1466 IncrementCharacterCount(length);
1467 }
1468
1469
1470 void AddString(Handle<String> string) {
1471 int length = string->length();
1472 ASSERT(length > 0);
1473 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001474 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001475 is_ascii_ = false;
1476 }
1477 IncrementCharacterCount(length);
1478 }
1479
1480
1481 Handle<String> ToString() {
1482 if (part_count_ == 0) {
1483 return Factory::empty_string();
1484 }
1485
1486 Handle<String> joined_string;
1487 if (is_ascii_) {
1488 joined_string = NewRawAsciiString(character_count_);
1489 AssertNoAllocation no_alloc;
1490 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1491 char* char_buffer = seq->GetChars();
1492 StringBuilderConcatHelper(*subject_,
1493 char_buffer,
1494 *parts_,
1495 part_count_);
1496 } else {
1497 // Non-ASCII.
1498 joined_string = NewRawTwoByteString(character_count_);
1499 AssertNoAllocation no_alloc;
1500 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1501 uc16* char_buffer = seq->GetChars();
1502 StringBuilderConcatHelper(*subject_,
1503 char_buffer,
1504 *parts_,
1505 part_count_);
1506 }
1507 return joined_string;
1508 }
1509
1510
1511 void IncrementCharacterCount(int by) {
1512 if (character_count_ > Smi::kMaxValue - by) {
1513 V8::FatalProcessOutOfMemory("String.replace result too large.");
1514 }
1515 character_count_ += by;
1516 }
1517
1518 private:
1519
1520 Handle<String> NewRawAsciiString(int size) {
1521 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1522 }
1523
1524
1525 Handle<String> NewRawTwoByteString(int size) {
1526 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1527 }
1528
1529
1530 void AddElement(Object* element) {
1531 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001532 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001533 parts_->set(part_count_, element);
1534 part_count_++;
1535 }
1536
1537 Handle<String> subject_;
1538 Handle<FixedArray> parts_;
1539 int part_count_;
1540 int character_count_;
1541 bool is_ascii_;
1542};
1543
1544
1545class CompiledReplacement {
1546 public:
1547 CompiledReplacement()
1548 : parts_(1), replacement_substrings_(0) {}
1549
1550 void Compile(Handle<String> replacement,
1551 int capture_count,
1552 int subject_length);
1553
1554 void Apply(ReplacementStringBuilder* builder,
1555 int match_from,
1556 int match_to,
1557 Handle<JSArray> last_match_info);
1558
1559 // Number of distinct parts of the replacement pattern.
1560 int parts() {
1561 return parts_.length();
1562 }
1563 private:
1564 enum PartType {
1565 SUBJECT_PREFIX = 1,
1566 SUBJECT_SUFFIX,
1567 SUBJECT_CAPTURE,
1568 REPLACEMENT_SUBSTRING,
1569 REPLACEMENT_STRING,
1570
1571 NUMBER_OF_PART_TYPES
1572 };
1573
1574 struct ReplacementPart {
1575 static inline ReplacementPart SubjectMatch() {
1576 return ReplacementPart(SUBJECT_CAPTURE, 0);
1577 }
1578 static inline ReplacementPart SubjectCapture(int capture_index) {
1579 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1580 }
1581 static inline ReplacementPart SubjectPrefix() {
1582 return ReplacementPart(SUBJECT_PREFIX, 0);
1583 }
1584 static inline ReplacementPart SubjectSuffix(int subject_length) {
1585 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1586 }
1587 static inline ReplacementPart ReplacementString() {
1588 return ReplacementPart(REPLACEMENT_STRING, 0);
1589 }
1590 static inline ReplacementPart ReplacementSubString(int from, int to) {
1591 ASSERT(from >= 0);
1592 ASSERT(to > from);
1593 return ReplacementPart(-from, to);
1594 }
1595
1596 // If tag <= 0 then it is the negation of a start index of a substring of
1597 // the replacement pattern, otherwise it's a value from PartType.
1598 ReplacementPart(int tag, int data)
1599 : tag(tag), data(data) {
1600 // Must be non-positive or a PartType value.
1601 ASSERT(tag < NUMBER_OF_PART_TYPES);
1602 }
1603 // Either a value of PartType or a non-positive number that is
1604 // the negation of an index into the replacement string.
1605 int tag;
1606 // The data value's interpretation depends on the value of tag:
1607 // tag == SUBJECT_PREFIX ||
1608 // tag == SUBJECT_SUFFIX: data is unused.
1609 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1610 // tag == REPLACEMENT_SUBSTRING ||
1611 // tag == REPLACEMENT_STRING: data is index into array of substrings
1612 // of the replacement string.
1613 // tag <= 0: Temporary representation of the substring of the replacement
1614 // string ranging over -tag .. data.
1615 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1616 // substring objects.
1617 int data;
1618 };
1619
1620 template<typename Char>
1621 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1622 Vector<Char> characters,
1623 int capture_count,
1624 int subject_length) {
1625 int length = characters.length();
1626 int last = 0;
1627 for (int i = 0; i < length; i++) {
1628 Char c = characters[i];
1629 if (c == '$') {
1630 int next_index = i + 1;
1631 if (next_index == length) { // No next character!
1632 break;
1633 }
1634 Char c2 = characters[next_index];
1635 switch (c2) {
1636 case '$':
1637 if (i > last) {
1638 // There is a substring before. Include the first "$".
1639 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1640 last = next_index + 1; // Continue after the second "$".
1641 } else {
1642 // Let the next substring start with the second "$".
1643 last = next_index;
1644 }
1645 i = next_index;
1646 break;
1647 case '`':
1648 if (i > last) {
1649 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1650 }
1651 parts->Add(ReplacementPart::SubjectPrefix());
1652 i = next_index;
1653 last = i + 1;
1654 break;
1655 case '\'':
1656 if (i > last) {
1657 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1658 }
1659 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1660 i = next_index;
1661 last = i + 1;
1662 break;
1663 case '&':
1664 if (i > last) {
1665 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1666 }
1667 parts->Add(ReplacementPart::SubjectMatch());
1668 i = next_index;
1669 last = i + 1;
1670 break;
1671 case '0':
1672 case '1':
1673 case '2':
1674 case '3':
1675 case '4':
1676 case '5':
1677 case '6':
1678 case '7':
1679 case '8':
1680 case '9': {
1681 int capture_ref = c2 - '0';
1682 if (capture_ref > capture_count) {
1683 i = next_index;
1684 continue;
1685 }
1686 int second_digit_index = next_index + 1;
1687 if (second_digit_index < length) {
1688 // Peek ahead to see if we have two digits.
1689 Char c3 = characters[second_digit_index];
1690 if ('0' <= c3 && c3 <= '9') { // Double digits.
1691 int double_digit_ref = capture_ref * 10 + c3 - '0';
1692 if (double_digit_ref <= capture_count) {
1693 next_index = second_digit_index;
1694 capture_ref = double_digit_ref;
1695 }
1696 }
1697 }
1698 if (capture_ref > 0) {
1699 if (i > last) {
1700 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1701 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001702 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001703 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1704 last = next_index + 1;
1705 }
1706 i = next_index;
1707 break;
1708 }
1709 default:
1710 i = next_index;
1711 break;
1712 }
1713 }
1714 }
1715 if (length > last) {
1716 if (last == 0) {
1717 parts->Add(ReplacementPart::ReplacementString());
1718 } else {
1719 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1720 }
1721 }
1722 }
1723
1724 ZoneList<ReplacementPart> parts_;
1725 ZoneList<Handle<String> > replacement_substrings_;
1726};
1727
1728
1729void CompiledReplacement::Compile(Handle<String> replacement,
1730 int capture_count,
1731 int subject_length) {
1732 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001733 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001734 AssertNoAllocation no_alloc;
1735 ParseReplacementPattern(&parts_,
1736 replacement->ToAsciiVector(),
1737 capture_count,
1738 subject_length);
1739 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001740 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001741 AssertNoAllocation no_alloc;
1742
1743 ParseReplacementPattern(&parts_,
1744 replacement->ToUC16Vector(),
1745 capture_count,
1746 subject_length);
1747 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001748 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001749 int substring_index = 0;
1750 for (int i = 0, n = parts_.length(); i < n; i++) {
1751 int tag = parts_[i].tag;
1752 if (tag <= 0) { // A replacement string slice.
1753 int from = -tag;
1754 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001755 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001756 parts_[i].tag = REPLACEMENT_SUBSTRING;
1757 parts_[i].data = substring_index;
1758 substring_index++;
1759 } else if (tag == REPLACEMENT_STRING) {
1760 replacement_substrings_.Add(replacement);
1761 parts_[i].data = substring_index;
1762 substring_index++;
1763 }
1764 }
1765}
1766
1767
1768void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1769 int match_from,
1770 int match_to,
1771 Handle<JSArray> last_match_info) {
1772 for (int i = 0, n = parts_.length(); i < n; i++) {
1773 ReplacementPart part = parts_[i];
1774 switch (part.tag) {
1775 case SUBJECT_PREFIX:
1776 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1777 break;
1778 case SUBJECT_SUFFIX: {
1779 int subject_length = part.data;
1780 if (match_to < subject_length) {
1781 builder->AddSubjectSlice(match_to, subject_length);
1782 }
1783 break;
1784 }
1785 case SUBJECT_CAPTURE: {
1786 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001787 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001788 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1789 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1790 if (from >= 0 && to > from) {
1791 builder->AddSubjectSlice(from, to);
1792 }
1793 break;
1794 }
1795 case REPLACEMENT_SUBSTRING:
1796 case REPLACEMENT_STRING:
1797 builder->AddString(replacement_substrings_[part.data]);
1798 break;
1799 default:
1800 UNREACHABLE();
1801 }
1802 }
1803}
1804
1805
1806
1807static Object* StringReplaceRegExpWithString(String* subject,
1808 JSRegExp* regexp,
1809 String* replacement,
1810 JSArray* last_match_info) {
1811 ASSERT(subject->IsFlat());
1812 ASSERT(replacement->IsFlat());
1813
1814 HandleScope handles;
1815
1816 int length = subject->length();
1817 Handle<String> subject_handle(subject);
1818 Handle<JSRegExp> regexp_handle(regexp);
1819 Handle<String> replacement_handle(replacement);
1820 Handle<JSArray> last_match_info_handle(last_match_info);
1821 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1822 subject_handle,
1823 0,
1824 last_match_info_handle);
1825 if (match.is_null()) {
1826 return Failure::Exception();
1827 }
1828 if (match->IsNull()) {
1829 return *subject_handle;
1830 }
1831
1832 int capture_count = regexp_handle->CaptureCount();
1833
1834 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001835 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001836 CompiledReplacement compiled_replacement;
1837 compiled_replacement.Compile(replacement_handle,
1838 capture_count,
1839 length);
1840
1841 bool is_global = regexp_handle->GetFlags().is_global();
1842
1843 // Guessing the number of parts that the final result string is built
1844 // from. Global regexps can match any number of times, so we guess
1845 // conservatively.
1846 int expected_parts =
1847 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1848 ReplacementStringBuilder builder(subject_handle, expected_parts);
1849
1850 // Index of end of last match.
1851 int prev = 0;
1852
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001853 // Number of parts added by compiled replacement plus preceeding
1854 // string and possibly suffix after last match. It is possible for
1855 // all components to use two elements when encoded as two smis.
1856 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001857 bool matched = true;
1858 do {
1859 ASSERT(last_match_info_handle->HasFastElements());
1860 // Increase the capacity of the builder before entering local handle-scope,
1861 // so its internal buffer can safely allocate a new handle if it grows.
1862 builder.EnsureCapacity(parts_added_per_loop);
1863
1864 HandleScope loop_scope;
1865 int start, end;
1866 {
1867 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001868 FixedArray* match_info_array =
1869 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001870
1871 ASSERT_EQ(capture_count * 2 + 2,
1872 RegExpImpl::GetLastCaptureCount(match_info_array));
1873 start = RegExpImpl::GetCapture(match_info_array, 0);
1874 end = RegExpImpl::GetCapture(match_info_array, 1);
1875 }
1876
1877 if (prev < start) {
1878 builder.AddSubjectSlice(prev, start);
1879 }
1880 compiled_replacement.Apply(&builder,
1881 start,
1882 end,
1883 last_match_info_handle);
1884 prev = end;
1885
1886 // Only continue checking for global regexps.
1887 if (!is_global) break;
1888
1889 // Continue from where the match ended, unless it was an empty match.
1890 int next = end;
1891 if (start == end) {
1892 next = end + 1;
1893 if (next > length) break;
1894 }
1895
1896 match = RegExpImpl::Exec(regexp_handle,
1897 subject_handle,
1898 next,
1899 last_match_info_handle);
1900 if (match.is_null()) {
1901 return Failure::Exception();
1902 }
1903 matched = !match->IsNull();
1904 } while (matched);
1905
1906 if (prev < length) {
1907 builder.AddSubjectSlice(prev, length);
1908 }
1909
1910 return *(builder.ToString());
1911}
1912
1913
1914static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1915 ASSERT(args.length() == 4);
1916
1917 CONVERT_CHECKED(String, subject, args[0]);
1918 if (!subject->IsFlat()) {
1919 Object* flat_subject = subject->TryFlatten();
1920 if (flat_subject->IsFailure()) {
1921 return flat_subject;
1922 }
1923 subject = String::cast(flat_subject);
1924 }
1925
1926 CONVERT_CHECKED(String, replacement, args[2]);
1927 if (!replacement->IsFlat()) {
1928 Object* flat_replacement = replacement->TryFlatten();
1929 if (flat_replacement->IsFailure()) {
1930 return flat_replacement;
1931 }
1932 replacement = String::cast(flat_replacement);
1933 }
1934
1935 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1936 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1937
1938 ASSERT(last_match_info->HasFastElements());
1939
1940 return StringReplaceRegExpWithString(subject,
1941 regexp,
1942 replacement,
1943 last_match_info);
1944}
1945
1946
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001947
ager@chromium.org7c537e22008-10-16 08:43:32 +00001948// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1949// limit, we can fix the size of tables.
1950static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001951// Reduce alphabet to this size.
1952static const int kBMAlphabetSize = 0x100;
1953// For patterns below this length, the skip length of Boyer-Moore is too short
1954// to compensate for the algorithmic overhead compared to simple brute force.
1955static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001956
ager@chromium.org7c537e22008-10-16 08:43:32 +00001957// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1958// shift. Only allows the last kBMMaxShift characters of the needle
1959// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001960class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001961 public:
1962 BMGoodSuffixBuffers() {}
1963 inline void init(int needle_length) {
1964 ASSERT(needle_length > 1);
1965 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1966 int len = needle_length - start;
1967 biased_suffixes_ = suffixes_ - start;
1968 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1969 for (int i = 0; i <= len; i++) {
1970 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001971 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001972 }
1973 inline int& suffix(int index) {
1974 ASSERT(biased_suffixes_ + index >= suffixes_);
1975 return biased_suffixes_[index];
1976 }
1977 inline int& shift(int index) {
1978 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1979 return biased_good_suffix_shift_[index];
1980 }
1981 private:
1982 int suffixes_[kBMMaxShift + 1];
1983 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001984 int* biased_suffixes_;
1985 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001986 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1987};
1988
1989// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001990static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001991static BMGoodSuffixBuffers bmgs_buffers;
1992
1993// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001994template <typename pchar>
1995static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1996 int start) {
1997 // Run forwards to populate bad_char_table, so that *last* instance
1998 // of character equivalence class is the one registered.
1999 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002000 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2001 : kBMAlphabetSize;
2002 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002003 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002004 } else {
2005 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002006 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002007 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002008 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002009 for (int i = start; i < pattern.length() - 1; i++) {
2010 pchar c = pattern[i];
2011 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002012 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002013 }
2014}
2015
2016template <typename pchar>
2017static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002018 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002019 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002020 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002021 // Compute Good Suffix tables.
2022 bmgs_buffers.init(m);
2023
2024 bmgs_buffers.shift(m-1) = 1;
2025 bmgs_buffers.suffix(m) = m + 1;
2026 pchar last_char = pattern[m - 1];
2027 int suffix = m + 1;
2028 for (int i = m; i > start;) {
2029 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2030 if (bmgs_buffers.shift(suffix) == len) {
2031 bmgs_buffers.shift(suffix) = suffix - i;
2032 }
2033 suffix = bmgs_buffers.suffix(suffix);
2034 }
2035 i--;
2036 suffix--;
2037 bmgs_buffers.suffix(i) = suffix;
2038 if (suffix == m) {
2039 // No suffix to extend, so we check against last_char only.
2040 while (i > start && pattern[i - 1] != last_char) {
2041 if (bmgs_buffers.shift(m) == len) {
2042 bmgs_buffers.shift(m) = m - i;
2043 }
2044 i--;
2045 bmgs_buffers.suffix(i) = m;
2046 }
2047 if (i > start) {
2048 i--;
2049 suffix--;
2050 bmgs_buffers.suffix(i) = suffix;
2051 }
2052 }
2053 }
2054 if (suffix < m) {
2055 for (int i = start; i <= m; i++) {
2056 if (bmgs_buffers.shift(i) == len) {
2057 bmgs_buffers.shift(i) = suffix - start;
2058 }
2059 if (i == suffix) {
2060 suffix = bmgs_buffers.suffix(suffix);
2061 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002062 }
2063 }
2064}
2065
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002066template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002067static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002068 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002069 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002070 }
2071 if (sizeof(pchar) == 1) {
2072 if (char_code > String::kMaxAsciiCharCode) {
2073 return -1;
2074 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002075 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002076 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002077 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002078}
2079
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002080// Restricted simplified Boyer-Moore string matching.
2081// Uses only the bad-shift table of Boyer-Moore and only uses it
2082// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002083template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002084static int BoyerMooreHorspool(Vector<const schar> subject,
2085 Vector<const pchar> pattern,
2086 int start_index,
2087 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002088 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002089 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002090 // Only preprocess at most kBMMaxShift last characters of pattern.
2091 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002092
ager@chromium.org7c537e22008-10-16 08:43:32 +00002093 BoyerMoorePopulateBadCharTable(pattern, start);
2094
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002095 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002096 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002097 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002098 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002099 // Perform search
2100 for (idx = start_index; idx <= n - m;) {
2101 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002102 int c;
2103 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002104 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002105 int shift = j - bc_occ;
2106 idx += shift;
2107 badness += 1 - shift; // at most zero, so badness cannot increase.
2108 if (idx > n - m) {
2109 *complete = true;
2110 return -1;
2111 }
2112 }
2113 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002114 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002115 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002116 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002117 return idx;
2118 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002119 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002120 // Badness increases by the number of characters we have
2121 // checked, and decreases by the number of characters we
2122 // can skip by shifting. It's a measure of how we are doing
2123 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002124 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002125 if (badness > 0) {
2126 *complete = false;
2127 return idx;
2128 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002129 }
2130 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002131 *complete = true;
2132 return -1;
2133}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002134
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002135
2136template <typename schar, typename pchar>
2137static int BoyerMooreIndexOf(Vector<const schar> subject,
2138 Vector<const pchar> pattern,
2139 int idx) {
2140 int n = subject.length();
2141 int m = pattern.length();
2142 // Only preprocess at most kBMMaxShift last characters of pattern.
2143 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2144
2145 // Build the Good Suffix table and continue searching.
2146 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2147 pchar last_char = pattern[m - 1];
2148 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002149 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002150 int j = m - 1;
2151 schar c;
2152 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002153 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002154 idx += shift;
2155 if (idx > n - m) {
2156 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002157 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002158 }
2159 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2160 if (j < 0) {
2161 return idx;
2162 } else if (j < start) {
2163 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002164 // Fall back on BMH shift.
2165 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002166 } else {
2167 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002168 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002169 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002170 if (gs_shift > shift) {
2171 shift = gs_shift;
2172 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002173 idx += shift;
2174 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002175 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002176
2177 return -1;
2178}
2179
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002180
2181template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002182static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002183 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002184 int start_index) {
2185 for (int i = start_index, n = string.length(); i < n; i++) {
2186 if (pattern_char == string[i]) {
2187 return i;
2188 }
2189 }
2190 return -1;
2191}
2192
2193// Trivial string search for shorter strings.
2194// On return, if "complete" is set to true, the return value is the
2195// final result of searching for the patter in the subject.
2196// If "complete" is set to false, the return value is the index where
2197// further checking should start, i.e., it's guaranteed that the pattern
2198// does not occur at a position prior to the returned index.
2199template <typename pchar, typename schar>
2200static int SimpleIndexOf(Vector<const schar> subject,
2201 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002202 int idx,
2203 bool* complete) {
2204 // Badness is a count of how much work we have done. When we have
2205 // done enough work we decide it's probably worth switching to a better
2206 // algorithm.
2207 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002208 // We know our pattern is at least 2 characters, we cache the first so
2209 // the common case of the first character not matching is faster.
2210 pchar pattern_first_char = pattern[0];
2211
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002212 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2213 badness++;
2214 if (badness > 0) {
2215 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002216 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002217 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002218 if (subject[i] != pattern_first_char) continue;
2219 int j = 1;
2220 do {
2221 if (pattern[j] != subject[i+j]) {
2222 break;
2223 }
2224 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002225 } while (j < pattern.length());
2226 if (j == pattern.length()) {
2227 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002228 return i;
2229 }
2230 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002231 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002232 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002233 return -1;
2234}
2235
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002236// Simple indexOf that never bails out. For short patterns only.
2237template <typename pchar, typename schar>
2238static int SimpleIndexOf(Vector<const schar> subject,
2239 Vector<const pchar> pattern,
2240 int idx) {
2241 pchar pattern_first_char = pattern[0];
2242 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2243 if (subject[i] != pattern_first_char) continue;
2244 int j = 1;
2245 do {
2246 if (pattern[j] != subject[i+j]) {
2247 break;
2248 }
2249 j++;
2250 } while (j < pattern.length());
2251 if (j == pattern.length()) {
2252 return i;
2253 }
2254 }
2255 return -1;
2256}
2257
2258
2259// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002260template <typename schar, typename pchar>
2261static int StringMatchStrategy(Vector<const schar> sub,
2262 Vector<const pchar> pat,
2263 int start_index) {
2264 ASSERT(pat.length() > 1);
2265
2266 // We have an ASCII haystack and a non-ASCII needle. Check if there
2267 // really is a non-ASCII character in the needle and bail out if there
2268 // is.
2269 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2270 for (int i = 0; i < pat.length(); i++) {
2271 uc16 c = pat[i];
2272 if (c > String::kMaxAsciiCharCode) {
2273 return -1;
2274 }
2275 }
2276 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002277 if (pat.length() < kBMMinPatternLength) {
2278 // We don't believe fancy searching can ever be more efficient.
2279 // The max shift of Boyer-Moore on a pattern of this length does
2280 // not compensate for the overhead.
2281 return SimpleIndexOf(sub, pat, start_index);
2282 }
2283 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002284 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002285 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2286 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002287 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002288 if (complete) return idx;
2289 return BoyerMooreIndexOf(sub, pat, idx);
2290}
2291
2292// Perform string match of pattern on subject, starting at start index.
2293// Caller must ensure that 0 <= start_index <= sub->length(),
2294// and should check that pat->length() + start_index <= sub->length()
2295int Runtime::StringMatch(Handle<String> sub,
2296 Handle<String> pat,
2297 int start_index) {
2298 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002299 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002300
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002301 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002302 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002303
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002304 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002305 if (start_index + pattern_length > subject_length) return -1;
2306
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002307 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002308 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002309 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002310 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002311 // character patterns linear search is necessary, so any smart
2312 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002313 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002314 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002315 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002316 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002317 if (pchar > String::kMaxAsciiCharCode) {
2318 return -1;
2319 }
2320 Vector<const char> ascii_vector =
2321 sub->ToAsciiVector().SubVector(start_index, subject_length);
2322 const void* pos = memchr(ascii_vector.start(),
2323 static_cast<const char>(pchar),
2324 static_cast<size_t>(ascii_vector.length()));
2325 if (pos == NULL) {
2326 return -1;
2327 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002328 return static_cast<int>(reinterpret_cast<const char*>(pos)
2329 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002330 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002331 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002332 }
2333
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002334 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002335 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002336 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002337
ager@chromium.org7c537e22008-10-16 08:43:32 +00002338 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2339 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002340 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002341 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002342 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002343 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002344 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002345 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002346 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002347 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002348 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002349 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002350 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002351 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002352}
2353
2354
2355static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002356 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002357 ASSERT(args.length() == 3);
2358
ager@chromium.org7c537e22008-10-16 08:43:32 +00002359 CONVERT_ARG_CHECKED(String, sub, 0);
2360 CONVERT_ARG_CHECKED(String, pat, 1);
2361
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002362 Object* index = args[2];
2363 uint32_t start_index;
2364 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2365
ager@chromium.org870a0b62008-11-04 11:43:05 +00002366 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002367 int position = Runtime::StringMatch(sub, pat, start_index);
2368 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002369}
2370
2371
2372static Object* Runtime_StringLastIndexOf(Arguments args) {
2373 NoHandleAllocation ha;
2374 ASSERT(args.length() == 3);
2375
2376 CONVERT_CHECKED(String, sub, args[0]);
2377 CONVERT_CHECKED(String, pat, args[1]);
2378 Object* index = args[2];
2379
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002380 sub->TryFlattenIfNotFlat();
2381 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002382
2383 uint32_t start_index;
2384 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2385
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002386 uint32_t pattern_length = pat->length();
2387 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002388
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002389 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002390 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002391 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002392
2393 for (int i = start_index; i >= 0; i--) {
2394 bool found = true;
2395 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002396 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002397 found = false;
2398 break;
2399 }
2400 }
2401 if (found) return Smi::FromInt(i);
2402 }
2403
2404 return Smi::FromInt(-1);
2405}
2406
2407
2408static Object* Runtime_StringLocaleCompare(Arguments args) {
2409 NoHandleAllocation ha;
2410 ASSERT(args.length() == 2);
2411
2412 CONVERT_CHECKED(String, str1, args[0]);
2413 CONVERT_CHECKED(String, str2, args[1]);
2414
2415 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002416 int str1_length = str1->length();
2417 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002418
2419 // Decide trivial cases without flattening.
2420 if (str1_length == 0) {
2421 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2422 return Smi::FromInt(-str2_length);
2423 } else {
2424 if (str2_length == 0) return Smi::FromInt(str1_length);
2425 }
2426
2427 int end = str1_length < str2_length ? str1_length : str2_length;
2428
2429 // No need to flatten if we are going to find the answer on the first
2430 // character. At this point we know there is at least one character
2431 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002432 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002433 if (d != 0) return Smi::FromInt(d);
2434
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002435 str1->TryFlattenIfNotFlat();
2436 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002437
2438 static StringInputBuffer buf1;
2439 static StringInputBuffer buf2;
2440
2441 buf1.Reset(str1);
2442 buf2.Reset(str2);
2443
2444 for (int i = 0; i < end; i++) {
2445 uint16_t char1 = buf1.GetNext();
2446 uint16_t char2 = buf2.GetNext();
2447 if (char1 != char2) return Smi::FromInt(char1 - char2);
2448 }
2449
2450 return Smi::FromInt(str1_length - str2_length);
2451}
2452
2453
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002454static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002455 NoHandleAllocation ha;
2456 ASSERT(args.length() == 3);
2457
2458 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002459 Object* from = args[1];
2460 Object* to = args[2];
2461 int start, end;
2462 // We have a fast integer-only case here to avoid a conversion to double in
2463 // the common case where from and to are Smis.
2464 if (from->IsSmi() && to->IsSmi()) {
2465 start = Smi::cast(from)->value();
2466 end = Smi::cast(to)->value();
2467 } else {
2468 CONVERT_DOUBLE_CHECKED(from_number, from);
2469 CONVERT_DOUBLE_CHECKED(to_number, to);
2470 start = FastD2I(from_number);
2471 end = FastD2I(to_number);
2472 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002473 RUNTIME_ASSERT(end >= start);
2474 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002475 RUNTIME_ASSERT(end <= value->length());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002476 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002477}
2478
2479
ager@chromium.org41826e72009-03-30 13:30:57 +00002480static Object* Runtime_StringMatch(Arguments args) {
2481 ASSERT_EQ(3, args.length());
2482
2483 CONVERT_ARG_CHECKED(String, subject, 0);
2484 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2485 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2486 HandleScope handles;
2487
2488 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2489
2490 if (match.is_null()) {
2491 return Failure::Exception();
2492 }
2493 if (match->IsNull()) {
2494 return Heap::null_value();
2495 }
2496 int length = subject->length();
2497
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002498 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002499 ZoneList<int> offsets(8);
2500 do {
2501 int start;
2502 int end;
2503 {
2504 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002505 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002506 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2507 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2508 }
2509 offsets.Add(start);
2510 offsets.Add(end);
2511 int index = start < end ? end : end + 1;
2512 if (index > length) break;
2513 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2514 if (match.is_null()) {
2515 return Failure::Exception();
2516 }
2517 } while (!match->IsNull());
2518 int matches = offsets.length() / 2;
2519 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2520 for (int i = 0; i < matches ; i++) {
2521 int from = offsets.at(i * 2);
2522 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002523 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002524 }
2525 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2526 result->set_length(Smi::FromInt(matches));
2527 return *result;
2528}
2529
2530
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002531static Object* Runtime_NumberToRadixString(Arguments args) {
2532 NoHandleAllocation ha;
2533 ASSERT(args.length() == 2);
2534
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002535 // Fast case where the result is a one character string.
2536 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2537 int value = Smi::cast(args[0])->value();
2538 int radix = Smi::cast(args[1])->value();
2539 if (value >= 0 && value < radix) {
2540 RUNTIME_ASSERT(radix <= 36);
2541 // Character array used for conversion.
2542 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2543 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2544 }
2545 }
2546
2547 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002548 CONVERT_DOUBLE_CHECKED(value, args[0]);
2549 if (isnan(value)) {
2550 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2551 }
2552 if (isinf(value)) {
2553 if (value < 0) {
2554 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2555 }
2556 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2557 }
2558 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2559 int radix = FastD2I(radix_number);
2560 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2561 char* str = DoubleToRadixCString(value, radix);
2562 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2563 DeleteArray(str);
2564 return result;
2565}
2566
2567
2568static Object* Runtime_NumberToFixed(Arguments args) {
2569 NoHandleAllocation ha;
2570 ASSERT(args.length() == 2);
2571
2572 CONVERT_DOUBLE_CHECKED(value, args[0]);
2573 if (isnan(value)) {
2574 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2575 }
2576 if (isinf(value)) {
2577 if (value < 0) {
2578 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2579 }
2580 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2581 }
2582 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2583 int f = FastD2I(f_number);
2584 RUNTIME_ASSERT(f >= 0);
2585 char* str = DoubleToFixedCString(value, f);
2586 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2587 DeleteArray(str);
2588 return res;
2589}
2590
2591
2592static Object* Runtime_NumberToExponential(Arguments args) {
2593 NoHandleAllocation ha;
2594 ASSERT(args.length() == 2);
2595
2596 CONVERT_DOUBLE_CHECKED(value, args[0]);
2597 if (isnan(value)) {
2598 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2599 }
2600 if (isinf(value)) {
2601 if (value < 0) {
2602 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2603 }
2604 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2605 }
2606 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2607 int f = FastD2I(f_number);
2608 RUNTIME_ASSERT(f >= -1 && f <= 20);
2609 char* str = DoubleToExponentialCString(value, f);
2610 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2611 DeleteArray(str);
2612 return res;
2613}
2614
2615
2616static Object* Runtime_NumberToPrecision(Arguments args) {
2617 NoHandleAllocation ha;
2618 ASSERT(args.length() == 2);
2619
2620 CONVERT_DOUBLE_CHECKED(value, args[0]);
2621 if (isnan(value)) {
2622 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2623 }
2624 if (isinf(value)) {
2625 if (value < 0) {
2626 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2627 }
2628 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2629 }
2630 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2631 int f = FastD2I(f_number);
2632 RUNTIME_ASSERT(f >= 1 && f <= 21);
2633 char* str = DoubleToPrecisionCString(value, f);
2634 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2635 DeleteArray(str);
2636 return res;
2637}
2638
2639
2640// Returns a single character string where first character equals
2641// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002642static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002643 if (index < static_cast<uint32_t>(string->length())) {
2644 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002645 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002646 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002647 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002648 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002649}
2650
2651
2652Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2653 // Handle [] indexing on Strings
2654 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002655 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2656 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002657 }
2658
2659 // Handle [] indexing on String objects
2660 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002661 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2662 Handle<Object> result =
2663 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2664 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002665 }
2666
2667 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002668 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002669 return prototype->GetElement(index);
2670 }
2671
2672 return object->GetElement(index);
2673}
2674
2675
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002676Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2677 HandleScope scope;
2678
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002679 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002680 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002681 Handle<Object> error =
2682 Factory::NewTypeError("non_object_property_load",
2683 HandleVector(args, 2));
2684 return Top::Throw(*error);
2685 }
2686
2687 // Check if the given key is an array index.
2688 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002689 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002690 return GetElementOrCharAt(object, index);
2691 }
2692
2693 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002694 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002695 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002696 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002697 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698 bool has_pending_exception = false;
2699 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002700 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002701 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002702 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002703 }
2704
ager@chromium.org32912102009-01-16 10:38:43 +00002705 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002706 // the element if so.
2707 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708 return GetElementOrCharAt(object, index);
2709 } else {
2710 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002711 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002712 }
2713}
2714
2715
2716static Object* Runtime_GetProperty(Arguments args) {
2717 NoHandleAllocation ha;
2718 ASSERT(args.length() == 2);
2719
2720 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002721 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002722
2723 return Runtime::GetObjectProperty(object, key);
2724}
2725
2726
ager@chromium.org7c537e22008-10-16 08:43:32 +00002727
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002728// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002729static Object* Runtime_KeyedGetProperty(Arguments args) {
2730 NoHandleAllocation ha;
2731 ASSERT(args.length() == 2);
2732
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002733 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002734 // itself.
2735 //
2736 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002737 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002738 // global proxy object never has properties. This is the case
2739 // because the global proxy object forwards everything to its hidden
2740 // prototype including local lookups.
2741 //
2742 // Additionally, we need to make sure that we do not cache results
2743 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002744 if (args[0]->IsJSObject() &&
2745 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002746 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002747 args[1]->IsString()) {
2748 JSObject* receiver = JSObject::cast(args[0]);
2749 String* key = String::cast(args[1]);
2750 if (receiver->HasFastProperties()) {
2751 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002752 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002753 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2754 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002755 Object* value = receiver->FastPropertyAt(offset);
2756 return value->IsTheHole() ? Heap::undefined_value() : value;
2757 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002758 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002759 LookupResult result;
2760 receiver->LocalLookup(key, &result);
2761 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2762 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002763 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002764 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002765 }
2766 } else {
2767 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002768 StringDictionary* dictionary = receiver->property_dictionary();
2769 int entry = dictionary->FindEntry(key);
2770 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002771 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002772 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002773 if (!receiver->IsGlobalObject()) return value;
2774 value = JSGlobalPropertyCell::cast(value)->value();
2775 if (!value->IsTheHole()) return value;
2776 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002777 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002778 }
2779 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002780
2781 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002782 return Runtime::GetObjectProperty(args.at<Object>(0),
2783 args.at<Object>(1));
2784}
2785
2786
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002787Object* Runtime::SetObjectProperty(Handle<Object> object,
2788 Handle<Object> key,
2789 Handle<Object> value,
2790 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002791 HandleScope scope;
2792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002793 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002794 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002795 Handle<Object> error =
2796 Factory::NewTypeError("non_object_property_store",
2797 HandleVector(args, 2));
2798 return Top::Throw(*error);
2799 }
2800
2801 // If the object isn't a JavaScript object, we ignore the store.
2802 if (!object->IsJSObject()) return *value;
2803
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002804 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2805
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002806 // Check if the given key is an array index.
2807 uint32_t index;
2808 if (Array::IndexFromObject(*key, &index)) {
2809 ASSERT(attr == NONE);
2810
2811 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2812 // of a string using [] notation. We need to support this too in
2813 // JavaScript.
2814 // In the case of a String object we just need to redirect the assignment to
2815 // the underlying string if the index is in range. Since the underlying
2816 // string does nothing with the assignment then we can ignore such
2817 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002818 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002819 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002820 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002821
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002822 Handle<Object> result = SetElement(js_object, index, value);
2823 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002824 return *value;
2825 }
2826
2827 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002828 Handle<Object> result;
2829 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002830 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002831 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002832 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002833 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002834 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002835 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002836 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002837 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002838 return *value;
2839 }
2840
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002842 bool has_pending_exception = false;
2843 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2844 if (has_pending_exception) return Failure::Exception();
2845 Handle<String> name = Handle<String>::cast(converted);
2846
2847 if (name->AsArrayIndex(&index)) {
2848 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002849 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002850 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002851 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002852 }
2853}
2854
2855
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002856Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2857 Handle<Object> key,
2858 Handle<Object> value,
2859 PropertyAttributes attr) {
2860 HandleScope scope;
2861
2862 // Check if the given key is an array index.
2863 uint32_t index;
2864 if (Array::IndexFromObject(*key, &index)) {
2865 ASSERT(attr == NONE);
2866
2867 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2868 // of a string using [] notation. We need to support this too in
2869 // JavaScript.
2870 // In the case of a String object we just need to redirect the assignment to
2871 // the underlying string if the index is in range. Since the underlying
2872 // string does nothing with the assignment then we can ignore such
2873 // assignments.
2874 if (js_object->IsStringObjectWithCharacterAt(index)) {
2875 return *value;
2876 }
2877
2878 return js_object->SetElement(index, *value);
2879 }
2880
2881 if (key->IsString()) {
2882 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2883 ASSERT(attr == NONE);
2884 return js_object->SetElement(index, *value);
2885 } else {
2886 Handle<String> key_string = Handle<String>::cast(key);
2887 key_string->TryFlattenIfNotFlat();
2888 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2889 *value,
2890 attr);
2891 }
2892 }
2893
2894 // Call-back into JavaScript to convert the key to a string.
2895 bool has_pending_exception = false;
2896 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2897 if (has_pending_exception) return Failure::Exception();
2898 Handle<String> name = Handle<String>::cast(converted);
2899
2900 if (name->AsArrayIndex(&index)) {
2901 ASSERT(attr == NONE);
2902 return js_object->SetElement(index, *value);
2903 } else {
2904 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2905 }
2906}
2907
2908
ager@chromium.orge2902be2009-06-08 12:21:35 +00002909Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2910 Handle<Object> key) {
2911 HandleScope scope;
2912
2913 // Check if the given key is an array index.
2914 uint32_t index;
2915 if (Array::IndexFromObject(*key, &index)) {
2916 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2917 // characters of a string using [] notation. In the case of a
2918 // String object we just need to redirect the deletion to the
2919 // underlying string if the index is in range. Since the
2920 // underlying string does nothing with the deletion, we can ignore
2921 // such deletions.
2922 if (js_object->IsStringObjectWithCharacterAt(index)) {
2923 return Heap::true_value();
2924 }
2925
2926 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2927 }
2928
2929 Handle<String> key_string;
2930 if (key->IsString()) {
2931 key_string = Handle<String>::cast(key);
2932 } else {
2933 // Call-back into JavaScript to convert the key to a string.
2934 bool has_pending_exception = false;
2935 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2936 if (has_pending_exception) return Failure::Exception();
2937 key_string = Handle<String>::cast(converted);
2938 }
2939
2940 key_string->TryFlattenIfNotFlat();
2941 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2942}
2943
2944
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002945static Object* Runtime_SetProperty(Arguments args) {
2946 NoHandleAllocation ha;
2947 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2948
2949 Handle<Object> object = args.at<Object>(0);
2950 Handle<Object> key = args.at<Object>(1);
2951 Handle<Object> value = args.at<Object>(2);
2952
2953 // Compute attributes.
2954 PropertyAttributes attributes = NONE;
2955 if (args.length() == 4) {
2956 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002957 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002958 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002959 RUNTIME_ASSERT(
2960 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2961 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002962 }
2963 return Runtime::SetObjectProperty(object, key, value, attributes);
2964}
2965
2966
2967// Set a local property, even if it is READ_ONLY. If the property does not
2968// exist, it will be added with attributes NONE.
2969static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2970 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002971 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002972 CONVERT_CHECKED(JSObject, object, args[0]);
2973 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002974 // Compute attributes.
2975 PropertyAttributes attributes = NONE;
2976 if (args.length() == 4) {
2977 CONVERT_CHECKED(Smi, value_obj, args[3]);
2978 int unchecked_value = value_obj->value();
2979 // Only attribute bits should be set.
2980 RUNTIME_ASSERT(
2981 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2982 attributes = static_cast<PropertyAttributes>(unchecked_value);
2983 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002984
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002985 return object->
2986 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002987}
2988
2989
2990static Object* Runtime_DeleteProperty(Arguments args) {
2991 NoHandleAllocation ha;
2992 ASSERT(args.length() == 2);
2993
2994 CONVERT_CHECKED(JSObject, object, args[0]);
2995 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002996 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002997}
2998
2999
ager@chromium.org9085a012009-05-11 19:22:57 +00003000static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
3001 Handle<String> key) {
3002 if (object->HasLocalProperty(*key)) return Heap::true_value();
3003 // Handle hidden prototypes. If there's a hidden prototype above this thing
3004 // then we have to check it for properties, because they are supposed to
3005 // look like they are on this object.
3006 Handle<Object> proto(object->GetPrototype());
3007 if (proto->IsJSObject() &&
3008 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
3009 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
3010 }
3011 return Heap::false_value();
3012}
3013
3014
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003015static Object* Runtime_HasLocalProperty(Arguments args) {
3016 NoHandleAllocation ha;
3017 ASSERT(args.length() == 2);
3018 CONVERT_CHECKED(String, key, args[1]);
3019
ager@chromium.org9085a012009-05-11 19:22:57 +00003020 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003021 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00003022 if (obj->IsJSObject()) {
3023 JSObject* object = JSObject::cast(obj);
3024 // Fast case - no interceptors.
3025 if (object->HasRealNamedProperty(key)) return Heap::true_value();
3026 // Slow case. Either it's not there or we have an interceptor. We should
3027 // have handles for this kind of deal.
3028 HandleScope scope;
3029 return HasLocalPropertyImplementation(Handle<JSObject>(object),
3030 Handle<String>(key));
3031 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003032 // Well, there is one exception: Handle [] on strings.
3033 uint32_t index;
3034 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00003035 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003036 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003037 return Heap::true_value();
3038 }
3039 }
3040 return Heap::false_value();
3041}
3042
3043
3044static Object* Runtime_HasProperty(Arguments args) {
3045 NoHandleAllocation na;
3046 ASSERT(args.length() == 2);
3047
3048 // Only JS objects can have properties.
3049 if (args[0]->IsJSObject()) {
3050 JSObject* object = JSObject::cast(args[0]);
3051 CONVERT_CHECKED(String, key, args[1]);
3052 if (object->HasProperty(key)) return Heap::true_value();
3053 }
3054 return Heap::false_value();
3055}
3056
3057
3058static Object* Runtime_HasElement(Arguments args) {
3059 NoHandleAllocation na;
3060 ASSERT(args.length() == 2);
3061
3062 // Only JS objects can have elements.
3063 if (args[0]->IsJSObject()) {
3064 JSObject* object = JSObject::cast(args[0]);
3065 CONVERT_CHECKED(Smi, index_obj, args[1]);
3066 uint32_t index = index_obj->value();
3067 if (object->HasElement(index)) return Heap::true_value();
3068 }
3069 return Heap::false_value();
3070}
3071
3072
3073static Object* Runtime_IsPropertyEnumerable(Arguments args) {
3074 NoHandleAllocation ha;
3075 ASSERT(args.length() == 2);
3076
3077 CONVERT_CHECKED(JSObject, object, args[0]);
3078 CONVERT_CHECKED(String, key, args[1]);
3079
3080 uint32_t index;
3081 if (key->AsArrayIndex(&index)) {
3082 return Heap::ToBoolean(object->HasElement(index));
3083 }
3084
ager@chromium.org870a0b62008-11-04 11:43:05 +00003085 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
3086 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003087}
3088
3089
3090static Object* Runtime_GetPropertyNames(Arguments args) {
3091 HandleScope scope;
3092 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003093 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003094 return *GetKeysFor(object);
3095}
3096
3097
3098// Returns either a FixedArray as Runtime_GetPropertyNames,
3099// or, if the given object has an enum cache that contains
3100// all enumerable properties of the object and its prototypes
3101// have none, the map of the object. This is used to speed up
3102// the check for deletions during a for-in.
3103static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3104 ASSERT(args.length() == 1);
3105
3106 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3107
3108 if (raw_object->IsSimpleEnum()) return raw_object->map();
3109
3110 HandleScope scope;
3111 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003112 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3113 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003114
3115 // Test again, since cache may have been built by preceding call.
3116 if (object->IsSimpleEnum()) return object->map();
3117
3118 return *content;
3119}
3120
3121
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003122static Object* Runtime_LocalKeys(Arguments args) {
3123 ASSERT_EQ(args.length(), 1);
3124 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3125 HandleScope scope;
3126 Handle<JSObject> object(raw_object);
3127 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3128 LOCAL_ONLY);
3129 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3130 // property array and since the result is mutable we have to create
3131 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003132 int length = contents->length();
3133 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3134 for (int i = 0; i < length; i++) {
3135 Object* entry = contents->get(i);
3136 if (entry->IsString()) {
3137 copy->set(i, entry);
3138 } else {
3139 ASSERT(entry->IsNumber());
3140 HandleScope scope;
3141 Handle<Object> entry_handle(entry);
3142 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3143 copy->set(i, *entry_str);
3144 }
3145 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003146 return *Factory::NewJSArrayWithElements(copy);
3147}
3148
3149
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003150static Object* Runtime_GetArgumentsProperty(Arguments args) {
3151 NoHandleAllocation ha;
3152 ASSERT(args.length() == 1);
3153
3154 // Compute the frame holding the arguments.
3155 JavaScriptFrameIterator it;
3156 it.AdvanceToArgumentsFrame();
3157 JavaScriptFrame* frame = it.frame();
3158
3159 // Get the actual number of provided arguments.
3160 const uint32_t n = frame->GetProvidedParametersCount();
3161
3162 // Try to convert the key to an index. If successful and within
3163 // index return the the argument from the frame.
3164 uint32_t index;
3165 if (Array::IndexFromObject(args[0], &index) && index < n) {
3166 return frame->GetParameter(index);
3167 }
3168
3169 // Convert the key to a string.
3170 HandleScope scope;
3171 bool exception = false;
3172 Handle<Object> converted =
3173 Execution::ToString(args.at<Object>(0), &exception);
3174 if (exception) return Failure::Exception();
3175 Handle<String> key = Handle<String>::cast(converted);
3176
3177 // Try to convert the string key into an array index.
3178 if (key->AsArrayIndex(&index)) {
3179 if (index < n) {
3180 return frame->GetParameter(index);
3181 } else {
3182 return Top::initial_object_prototype()->GetElement(index);
3183 }
3184 }
3185
3186 // Handle special arguments properties.
3187 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3188 if (key->Equals(Heap::callee_symbol())) return frame->function();
3189
3190 // Lookup in the initial Object.prototype object.
3191 return Top::initial_object_prototype()->GetProperty(*key);
3192}
3193
3194
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003195static Object* Runtime_ToFastProperties(Arguments args) {
3196 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003197 Handle<Object> object = args.at<Object>(0);
3198 if (object->IsJSObject()) {
3199 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3200 js_object->TransformToFastProperties(0);
3201 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003202 return *object;
3203}
3204
3205
3206static Object* Runtime_ToSlowProperties(Arguments args) {
3207 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003208 Handle<Object> object = args.at<Object>(0);
3209 if (object->IsJSObject()) {
3210 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003211 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003212 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003213 return *object;
3214}
3215
3216
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003217static Object* Runtime_ToBool(Arguments args) {
3218 NoHandleAllocation ha;
3219 ASSERT(args.length() == 1);
3220
3221 return args[0]->ToBoolean();
3222}
3223
3224
3225// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3226// Possible optimizations: put the type string into the oddballs.
3227static Object* Runtime_Typeof(Arguments args) {
3228 NoHandleAllocation ha;
3229
3230 Object* obj = args[0];
3231 if (obj->IsNumber()) return Heap::number_symbol();
3232 HeapObject* heap_obj = HeapObject::cast(obj);
3233
3234 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003235 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003236
3237 InstanceType instance_type = heap_obj->map()->instance_type();
3238 if (instance_type < FIRST_NONSTRING_TYPE) {
3239 return Heap::string_symbol();
3240 }
3241
3242 switch (instance_type) {
3243 case ODDBALL_TYPE:
3244 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3245 return Heap::boolean_symbol();
3246 }
3247 if (heap_obj->IsNull()) {
3248 return Heap::object_symbol();
3249 }
3250 ASSERT(heap_obj->IsUndefined());
3251 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003252 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003253 return Heap::function_symbol();
3254 default:
3255 // For any kind of object not handled above, the spec rule for
3256 // host objects gives that it is okay to return "object"
3257 return Heap::object_symbol();
3258 }
3259}
3260
3261
3262static Object* Runtime_StringToNumber(Arguments args) {
3263 NoHandleAllocation ha;
3264 ASSERT(args.length() == 1);
3265 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003266 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003267 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3268}
3269
3270
3271static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3272 NoHandleAllocation ha;
3273 ASSERT(args.length() == 1);
3274
3275 CONVERT_CHECKED(JSArray, codes, args[0]);
3276 int length = Smi::cast(codes->length())->value();
3277
3278 // Check if the string can be ASCII.
3279 int i;
3280 for (i = 0; i < length; i++) {
3281 Object* element = codes->GetElement(i);
3282 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3283 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3284 break;
3285 }
3286
3287 Object* object = NULL;
3288 if (i == length) { // The string is ASCII.
3289 object = Heap::AllocateRawAsciiString(length);
3290 } else { // The string is not ASCII.
3291 object = Heap::AllocateRawTwoByteString(length);
3292 }
3293
3294 if (object->IsFailure()) return object;
3295 String* result = String::cast(object);
3296 for (int i = 0; i < length; i++) {
3297 Object* element = codes->GetElement(i);
3298 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003299 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003300 }
3301 return result;
3302}
3303
3304
3305// kNotEscaped is generated by the following:
3306//
3307// #!/bin/perl
3308// for (my $i = 0; $i < 256; $i++) {
3309// print "\n" if $i % 16 == 0;
3310// my $c = chr($i);
3311// my $escaped = 1;
3312// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3313// print $escaped ? "0, " : "1, ";
3314// }
3315
3316
3317static bool IsNotEscaped(uint16_t character) {
3318 // Only for 8 bit characters, the rest are always escaped (in a different way)
3319 ASSERT(character < 256);
3320 static const char kNotEscaped[256] = {
3321 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3322 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3323 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3324 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3325 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3326 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3327 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3328 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3329 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3330 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3331 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3332 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3333 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3334 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3335 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3336 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3337 };
3338 return kNotEscaped[character] != 0;
3339}
3340
3341
3342static Object* Runtime_URIEscape(Arguments args) {
3343 const char hex_chars[] = "0123456789ABCDEF";
3344 NoHandleAllocation ha;
3345 ASSERT(args.length() == 1);
3346 CONVERT_CHECKED(String, source, args[0]);
3347
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003348 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003349
3350 int escaped_length = 0;
3351 int length = source->length();
3352 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003353 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003354 buffer->Reset(source);
3355 while (buffer->has_more()) {
3356 uint16_t character = buffer->GetNext();
3357 if (character >= 256) {
3358 escaped_length += 6;
3359 } else if (IsNotEscaped(character)) {
3360 escaped_length++;
3361 } else {
3362 escaped_length += 3;
3363 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003364 // We don't allow strings that are longer than a maximal length.
3365 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003366 Top::context()->mark_out_of_memory();
3367 return Failure::OutOfMemoryException();
3368 }
3369 }
3370 }
3371 // No length change implies no change. Return original string if no change.
3372 if (escaped_length == length) {
3373 return source;
3374 }
3375 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3376 if (o->IsFailure()) return o;
3377 String* destination = String::cast(o);
3378 int dest_position = 0;
3379
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003380 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003381 buffer->Rewind();
3382 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003383 uint16_t chr = buffer->GetNext();
3384 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003385 destination->Set(dest_position, '%');
3386 destination->Set(dest_position+1, 'u');
3387 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3388 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3389 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3390 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003391 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003392 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003393 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003394 dest_position++;
3395 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003396 destination->Set(dest_position, '%');
3397 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3398 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003399 dest_position += 3;
3400 }
3401 }
3402 return destination;
3403}
3404
3405
3406static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3407 static const signed char kHexValue['g'] = {
3408 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3409 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3410 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3411 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3412 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3413 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3414 -1, 10, 11, 12, 13, 14, 15 };
3415
3416 if (character1 > 'f') return -1;
3417 int hi = kHexValue[character1];
3418 if (hi == -1) return -1;
3419 if (character2 > 'f') return -1;
3420 int lo = kHexValue[character2];
3421 if (lo == -1) return -1;
3422 return (hi << 4) + lo;
3423}
3424
3425
ager@chromium.org870a0b62008-11-04 11:43:05 +00003426static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003427 int i,
3428 int length,
3429 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003430 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003431 int32_t hi = 0;
3432 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003433 if (character == '%' &&
3434 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003435 source->Get(i + 1) == 'u' &&
3436 (hi = TwoDigitHex(source->Get(i + 2),
3437 source->Get(i + 3))) != -1 &&
3438 (lo = TwoDigitHex(source->Get(i + 4),
3439 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003440 *step = 6;
3441 return (hi << 8) + lo;
3442 } else if (character == '%' &&
3443 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003444 (lo = TwoDigitHex(source->Get(i + 1),
3445 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003446 *step = 3;
3447 return lo;
3448 } else {
3449 *step = 1;
3450 return character;
3451 }
3452}
3453
3454
3455static Object* Runtime_URIUnescape(Arguments args) {
3456 NoHandleAllocation ha;
3457 ASSERT(args.length() == 1);
3458 CONVERT_CHECKED(String, source, args[0]);
3459
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003460 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003461
3462 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003463 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003464
3465 int unescaped_length = 0;
3466 for (int i = 0; i < length; unescaped_length++) {
3467 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003468 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003469 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003470 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003471 i += step;
3472 }
3473
3474 // No length change implies no change. Return original string if no change.
3475 if (unescaped_length == length)
3476 return source;
3477
3478 Object* o = ascii ?
3479 Heap::AllocateRawAsciiString(unescaped_length) :
3480 Heap::AllocateRawTwoByteString(unescaped_length);
3481 if (o->IsFailure()) return o;
3482 String* destination = String::cast(o);
3483
3484 int dest_position = 0;
3485 for (int i = 0; i < length; dest_position++) {
3486 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003487 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003488 i += step;
3489 }
3490 return destination;
3491}
3492
3493
3494static Object* Runtime_StringParseInt(Arguments args) {
3495 NoHandleAllocation ha;
3496
3497 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003498 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003499
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003500 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003501
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003502 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003503 int i;
3504
3505 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003506 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003507 if (i == len) return Heap::nan_value();
3508
3509 // Compute the sign (default to +).
3510 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003511 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003512 sign = -1;
3513 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003514 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003515 i++;
3516 }
3517
3518 // Compute the radix if 0.
3519 if (radix == 0) {
3520 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003521 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003522 radix = 8;
3523 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003524 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003525 if (c == 'x' || c == 'X') {
3526 radix = 16;
3527 i += 2;
3528 }
3529 }
3530 }
3531 } else if (radix == 16) {
3532 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003533 if (i + 1 < len && s->Get(i) == '0') {
3534 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003535 if (c == 'x' || c == 'X') i += 2;
3536 }
3537 }
3538
3539 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3540 double value;
3541 int end_index = StringToInt(s, i, radix, &value);
3542 if (end_index != i) {
3543 return Heap::NumberFromDouble(sign * value);
3544 }
3545 return Heap::nan_value();
3546}
3547
3548
3549static Object* Runtime_StringParseFloat(Arguments args) {
3550 NoHandleAllocation ha;
3551 CONVERT_CHECKED(String, str, args[0]);
3552
3553 // ECMA-262 section 15.1.2.3, empty string is NaN
3554 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3555
3556 // Create a number object from the value.
3557 return Heap::NumberFromDouble(value);
3558}
3559
3560
3561static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3562static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3563
3564
3565template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003566static Object* ConvertCaseHelper(String* s,
3567 int length,
3568 int input_string_length,
3569 unibrow::Mapping<Converter, 128>* mapping) {
3570 // We try this twice, once with the assumption that the result is no longer
3571 // than the input and, if that assumption breaks, again with the exact
3572 // length. This may not be pretty, but it is nicer than what was here before
3573 // and I hereby claim my vaffel-is.
3574 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003575 // Allocate the resulting string.
3576 //
3577 // NOTE: This assumes that the upper/lower case of an ascii
3578 // character is also ascii. This is currently the case, but it
3579 // might break in the future if we implement more context and locale
3580 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003581 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003582 ? Heap::AllocateRawAsciiString(length)
3583 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003584 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003585 String* result = String::cast(o);
3586 bool has_changed_character = false;
3587
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003588 // Convert all characters to upper case, assuming that they will fit
3589 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003590 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003591 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003592 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003593 // We can assume that the string is not empty
3594 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003595 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003596 bool has_next = buffer->has_more();
3597 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003598 int char_length = mapping->get(current, next, chars);
3599 if (char_length == 0) {
3600 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003601 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003602 i++;
3603 } else if (char_length == 1) {
3604 // Common case: converting the letter resulted in one character.
3605 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003606 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607 has_changed_character = true;
3608 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003609 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003610 // We've assumed that the result would be as long as the
3611 // input but here is a character that converts to several
3612 // characters. No matter, we calculate the exact length
3613 // of the result and try the whole thing again.
3614 //
3615 // Note that this leaves room for optimization. We could just
3616 // memcpy what we already have to the result string. Also,
3617 // the result string is the last object allocated we could
3618 // "realloc" it and probably, in the vast majority of cases,
3619 // extend the existing string to be able to hold the full
3620 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003621 int next_length = 0;
3622 if (has_next) {
3623 next_length = mapping->get(next, 0, chars);
3624 if (next_length == 0) next_length = 1;
3625 }
3626 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003627 while (buffer->has_more()) {
3628 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003629 // NOTE: we use 0 as the next character here because, while
3630 // the next character may affect what a character converts to,
3631 // it does not in any case affect the length of what it convert
3632 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003633 int char_length = mapping->get(current, 0, chars);
3634 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003635 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003636 if (current_length > Smi::kMaxValue) {
3637 Top::context()->mark_out_of_memory();
3638 return Failure::OutOfMemoryException();
3639 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003640 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003641 // Try again with the real length.
3642 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003643 } else {
3644 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003645 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003646 i++;
3647 }
3648 has_changed_character = true;
3649 }
3650 current = next;
3651 }
3652 if (has_changed_character) {
3653 return result;
3654 } else {
3655 // If we didn't actually change anything in doing the conversion
3656 // we simple return the result and let the converted string
3657 // become garbage; there is no reason to keep two identical strings
3658 // alive.
3659 return s;
3660 }
3661}
3662
3663
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003664template <class Converter>
3665static Object* ConvertCase(Arguments args,
3666 unibrow::Mapping<Converter, 128>* mapping) {
3667 NoHandleAllocation ha;
3668
3669 CONVERT_CHECKED(String, s, args[0]);
3670 s->TryFlattenIfNotFlat();
3671
3672 int input_string_length = s->length();
3673 // Assume that the string is not empty; we need this assumption later
3674 if (input_string_length == 0) return s;
3675 int length = input_string_length;
3676
3677 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3678 if (answer->IsSmi()) {
3679 // Retry with correct length.
3680 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3681 }
3682 return answer; // This may be a failure.
3683}
3684
3685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686static Object* Runtime_StringToLowerCase(Arguments args) {
3687 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3688}
3689
3690
3691static Object* Runtime_StringToUpperCase(Arguments args) {
3692 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3693}
3694
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003695static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
3696 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
3697}
3698
3699static Object* Runtime_StringTrim(Arguments args) {
3700 NoHandleAllocation ha;
3701 ASSERT(args.length() == 3);
3702
3703 CONVERT_CHECKED(String, s, args[0]);
3704 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
3705 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
3706
3707 s->TryFlattenIfNotFlat();
3708 int length = s->length();
3709
3710 int left = 0;
3711 if (trimLeft) {
3712 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
3713 left++;
3714 }
3715 }
3716
3717 int right = length;
3718 if (trimRight) {
3719 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
3720 right--;
3721 }
3722 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003723 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003724}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003726bool Runtime::IsUpperCaseChar(uint16_t ch) {
3727 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3728 int char_length = to_upper_mapping.get(ch, 0, chars);
3729 return char_length == 0;
3730}
3731
3732
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003733static Object* Runtime_NumberToString(Arguments args) {
3734 NoHandleAllocation ha;
3735 ASSERT(args.length() == 1);
3736
3737 Object* number = args[0];
3738 RUNTIME_ASSERT(number->IsNumber());
3739
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003740 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003741}
3742
3743
3744static Object* Runtime_NumberToInteger(Arguments args) {
3745 NoHandleAllocation ha;
3746 ASSERT(args.length() == 1);
3747
3748 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003749 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003750 CONVERT_DOUBLE_CHECKED(number, obj);
3751 return Heap::NumberFromDouble(DoubleToInteger(number));
3752}
3753
3754
3755static Object* Runtime_NumberToJSUint32(Arguments args) {
3756 NoHandleAllocation ha;
3757 ASSERT(args.length() == 1);
3758
3759 Object* obj = args[0];
3760 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3761 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3762 return Heap::NumberFromUint32(number);
3763}
3764
3765
3766static Object* Runtime_NumberToJSInt32(Arguments args) {
3767 NoHandleAllocation ha;
3768 ASSERT(args.length() == 1);
3769
3770 Object* obj = args[0];
3771 if (obj->IsSmi()) return obj;
3772 CONVERT_DOUBLE_CHECKED(number, obj);
3773 return Heap::NumberFromInt32(DoubleToInt32(number));
3774}
3775
3776
ager@chromium.org870a0b62008-11-04 11:43:05 +00003777// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3778// a small integer.
3779static Object* Runtime_NumberToSmi(Arguments args) {
3780 NoHandleAllocation ha;
3781 ASSERT(args.length() == 1);
3782
3783 Object* obj = args[0];
3784 if (obj->IsSmi()) {
3785 return obj;
3786 }
3787 if (obj->IsHeapNumber()) {
3788 double value = HeapNumber::cast(obj)->value();
3789 int int_value = FastD2I(value);
3790 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3791 return Smi::FromInt(int_value);
3792 }
3793 }
3794 return Heap::nan_value();
3795}
3796
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003797
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003798static Object* Runtime_NumberAdd(Arguments args) {
3799 NoHandleAllocation ha;
3800 ASSERT(args.length() == 2);
3801
3802 CONVERT_DOUBLE_CHECKED(x, args[0]);
3803 CONVERT_DOUBLE_CHECKED(y, args[1]);
3804 return Heap::AllocateHeapNumber(x + y);
3805}
3806
3807
3808static Object* Runtime_NumberSub(Arguments args) {
3809 NoHandleAllocation ha;
3810 ASSERT(args.length() == 2);
3811
3812 CONVERT_DOUBLE_CHECKED(x, args[0]);
3813 CONVERT_DOUBLE_CHECKED(y, args[1]);
3814 return Heap::AllocateHeapNumber(x - y);
3815}
3816
3817
3818static Object* Runtime_NumberMul(Arguments args) {
3819 NoHandleAllocation ha;
3820 ASSERT(args.length() == 2);
3821
3822 CONVERT_DOUBLE_CHECKED(x, args[0]);
3823 CONVERT_DOUBLE_CHECKED(y, args[1]);
3824 return Heap::AllocateHeapNumber(x * y);
3825}
3826
3827
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003828static Object* Runtime_NumberUnaryMinus(Arguments args) {
3829 NoHandleAllocation ha;
3830 ASSERT(args.length() == 1);
3831
3832 CONVERT_DOUBLE_CHECKED(x, args[0]);
3833 return Heap::AllocateHeapNumber(-x);
3834}
3835
3836
3837static Object* Runtime_NumberDiv(Arguments args) {
3838 NoHandleAllocation ha;
3839 ASSERT(args.length() == 2);
3840
3841 CONVERT_DOUBLE_CHECKED(x, args[0]);
3842 CONVERT_DOUBLE_CHECKED(y, args[1]);
3843 return Heap::NewNumberFromDouble(x / y);
3844}
3845
3846
3847static Object* Runtime_NumberMod(Arguments args) {
3848 NoHandleAllocation ha;
3849 ASSERT(args.length() == 2);
3850
3851 CONVERT_DOUBLE_CHECKED(x, args[0]);
3852 CONVERT_DOUBLE_CHECKED(y, args[1]);
3853
ager@chromium.org3811b432009-10-28 14:53:37 +00003854 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003855 // NewNumberFromDouble may return a Smi instead of a Number object
3856 return Heap::NewNumberFromDouble(x);
3857}
3858
3859
3860static Object* Runtime_StringAdd(Arguments args) {
3861 NoHandleAllocation ha;
3862 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003863 CONVERT_CHECKED(String, str1, args[0]);
3864 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003865 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003866 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003867}
3868
3869
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003870template<typename sinkchar>
3871static inline void StringBuilderConcatHelper(String* special,
3872 sinkchar* sink,
3873 FixedArray* fixed_array,
3874 int array_length) {
3875 int position = 0;
3876 for (int i = 0; i < array_length; i++) {
3877 Object* element = fixed_array->get(i);
3878 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003879 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003880 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003881 int pos;
3882 int len;
3883 if (encoded_slice > 0) {
3884 // Position and length encoded in one smi.
3885 pos = StringBuilderSubstringPosition::decode(encoded_slice);
3886 len = StringBuilderSubstringLength::decode(encoded_slice);
3887 } else {
3888 // Position and length encoded in two smis.
3889 Object* obj = fixed_array->get(++i);
3890 ASSERT(obj->IsSmi());
3891 pos = Smi::cast(obj)->value();
3892 len = -encoded_slice;
3893 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00003894 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003895 sink + position,
3896 pos,
3897 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003898 position += len;
3899 } else {
3900 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003901 int element_length = string->length();
3902 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003903 position += element_length;
3904 }
3905 }
3906}
3907
3908
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003909static Object* Runtime_StringBuilderConcat(Arguments args) {
3910 NoHandleAllocation ha;
3911 ASSERT(args.length() == 2);
3912 CONVERT_CHECKED(JSArray, array, args[0]);
3913 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003914
3915 // This assumption is used by the slice encoding in one or two smis.
3916 ASSERT(Smi::kMaxValue >= String::kMaxLength);
3917
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003918 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003919 Object* smi_array_length = array->length();
3920 if (!smi_array_length->IsSmi()) {
3921 Top::context()->mark_out_of_memory();
3922 return Failure::OutOfMemoryException();
3923 }
3924 int array_length = Smi::cast(smi_array_length)->value();
3925 if (!array->HasFastElements()) {
3926 return Top::Throw(Heap::illegal_argument_symbol());
3927 }
3928 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003929 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003930 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003931 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003932
3933 if (array_length == 0) {
3934 return Heap::empty_string();
3935 } else if (array_length == 1) {
3936 Object* first = fixed_array->get(0);
3937 if (first->IsString()) return first;
3938 }
3939
ager@chromium.org5ec48922009-05-05 07:25:34 +00003940 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003941 int position = 0;
3942 for (int i = 0; i < array_length; i++) {
3943 Object* elt = fixed_array->get(i);
3944 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003945 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003947 if (len > 0) {
3948 // Position and length encoded in one smi.
3949 int pos = len >> 11;
3950 len &= 0x7ff;
3951 if (pos + len > special_length) {
3952 return Top::Throw(Heap::illegal_argument_symbol());
3953 }
3954 position += len;
3955 } else {
3956 // Position and length encoded in two smis.
3957 position += (-len);
3958 // Get the position and check that it is also a smi.
3959 i++;
3960 if (i >= array_length) {
3961 return Top::Throw(Heap::illegal_argument_symbol());
3962 }
3963 Object* pos = fixed_array->get(i);
3964 if (!pos->IsSmi()) {
3965 return Top::Throw(Heap::illegal_argument_symbol());
3966 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003967 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003968 } else if (elt->IsString()) {
3969 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003970 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003971 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003972 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003973 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003974 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003975 } else {
3976 return Top::Throw(Heap::illegal_argument_symbol());
3977 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003978 if (position > String::kMaxLength) {
3979 Top::context()->mark_out_of_memory();
3980 return Failure::OutOfMemoryException();
3981 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003982 }
3983
3984 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003985 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003986
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003987 if (ascii) {
3988 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003989 if (object->IsFailure()) return object;
3990 SeqAsciiString* answer = SeqAsciiString::cast(object);
3991 StringBuilderConcatHelper(special,
3992 answer->GetChars(),
3993 fixed_array,
3994 array_length);
3995 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003996 } else {
3997 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003998 if (object->IsFailure()) return object;
3999 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
4000 StringBuilderConcatHelper(special,
4001 answer->GetChars(),
4002 fixed_array,
4003 array_length);
4004 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004005 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004006}
4007
4008
4009static Object* Runtime_NumberOr(Arguments args) {
4010 NoHandleAllocation ha;
4011 ASSERT(args.length() == 2);
4012
4013 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4014 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4015 return Heap::NumberFromInt32(x | y);
4016}
4017
4018
4019static Object* Runtime_NumberAnd(Arguments args) {
4020 NoHandleAllocation ha;
4021 ASSERT(args.length() == 2);
4022
4023 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4024 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4025 return Heap::NumberFromInt32(x & y);
4026}
4027
4028
4029static Object* Runtime_NumberXor(Arguments args) {
4030 NoHandleAllocation ha;
4031 ASSERT(args.length() == 2);
4032
4033 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4034 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4035 return Heap::NumberFromInt32(x ^ y);
4036}
4037
4038
4039static Object* Runtime_NumberNot(Arguments args) {
4040 NoHandleAllocation ha;
4041 ASSERT(args.length() == 1);
4042
4043 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4044 return Heap::NumberFromInt32(~x);
4045}
4046
4047
4048static Object* Runtime_NumberShl(Arguments args) {
4049 NoHandleAllocation ha;
4050 ASSERT(args.length() == 2);
4051
4052 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4053 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4054 return Heap::NumberFromInt32(x << (y & 0x1f));
4055}
4056
4057
4058static Object* Runtime_NumberShr(Arguments args) {
4059 NoHandleAllocation ha;
4060 ASSERT(args.length() == 2);
4061
4062 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
4063 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4064 return Heap::NumberFromUint32(x >> (y & 0x1f));
4065}
4066
4067
4068static Object* Runtime_NumberSar(Arguments args) {
4069 NoHandleAllocation ha;
4070 ASSERT(args.length() == 2);
4071
4072 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4073 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4074 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
4075}
4076
4077
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004078static Object* Runtime_NumberEquals(Arguments args) {
4079 NoHandleAllocation ha;
4080 ASSERT(args.length() == 2);
4081
4082 CONVERT_DOUBLE_CHECKED(x, args[0]);
4083 CONVERT_DOUBLE_CHECKED(y, args[1]);
4084 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
4085 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
4086 if (x == y) return Smi::FromInt(EQUAL);
4087 Object* result;
4088 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
4089 result = Smi::FromInt(EQUAL);
4090 } else {
4091 result = Smi::FromInt(NOT_EQUAL);
4092 }
4093 return result;
4094}
4095
4096
4097static Object* Runtime_StringEquals(Arguments args) {
4098 NoHandleAllocation ha;
4099 ASSERT(args.length() == 2);
4100
4101 CONVERT_CHECKED(String, x, args[0]);
4102 CONVERT_CHECKED(String, y, args[1]);
4103
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004104 bool not_equal = !x->Equals(y);
4105 // This is slightly convoluted because the value that signifies
4106 // equality is 0 and inequality is 1 so we have to negate the result
4107 // from String::Equals.
4108 ASSERT(not_equal == 0 || not_equal == 1);
4109 STATIC_CHECK(EQUAL == 0);
4110 STATIC_CHECK(NOT_EQUAL == 1);
4111 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004112}
4113
4114
4115static Object* Runtime_NumberCompare(Arguments args) {
4116 NoHandleAllocation ha;
4117 ASSERT(args.length() == 3);
4118
4119 CONVERT_DOUBLE_CHECKED(x, args[0]);
4120 CONVERT_DOUBLE_CHECKED(y, args[1]);
4121 if (isnan(x) || isnan(y)) return args[2];
4122 if (x == y) return Smi::FromInt(EQUAL);
4123 if (isless(x, y)) return Smi::FromInt(LESS);
4124 return Smi::FromInt(GREATER);
4125}
4126
4127
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004128// Compare two Smis as if they were converted to strings and then
4129// compared lexicographically.
4130static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4131 NoHandleAllocation ha;
4132 ASSERT(args.length() == 2);
4133
4134 // Arrays for the individual characters of the two Smis. Smis are
4135 // 31 bit integers and 10 decimal digits are therefore enough.
4136 static int x_elms[10];
4137 static int y_elms[10];
4138
4139 // Extract the integer values from the Smis.
4140 CONVERT_CHECKED(Smi, x, args[0]);
4141 CONVERT_CHECKED(Smi, y, args[1]);
4142 int x_value = x->value();
4143 int y_value = y->value();
4144
4145 // If the integers are equal so are the string representations.
4146 if (x_value == y_value) return Smi::FromInt(EQUAL);
4147
4148 // If one of the integers are zero the normal integer order is the
4149 // same as the lexicographic order of the string representations.
4150 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4151
ager@chromium.org32912102009-01-16 10:38:43 +00004152 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004153 // smallest because the char code of '-' is less than the char code
4154 // of any digit. Otherwise, we make both values positive.
4155 if (x_value < 0 || y_value < 0) {
4156 if (y_value >= 0) return Smi::FromInt(LESS);
4157 if (x_value >= 0) return Smi::FromInt(GREATER);
4158 x_value = -x_value;
4159 y_value = -y_value;
4160 }
4161
4162 // Convert the integers to arrays of their decimal digits.
4163 int x_index = 0;
4164 int y_index = 0;
4165 while (x_value > 0) {
4166 x_elms[x_index++] = x_value % 10;
4167 x_value /= 10;
4168 }
4169 while (y_value > 0) {
4170 y_elms[y_index++] = y_value % 10;
4171 y_value /= 10;
4172 }
4173
4174 // Loop through the arrays of decimal digits finding the first place
4175 // where they differ.
4176 while (--x_index >= 0 && --y_index >= 0) {
4177 int diff = x_elms[x_index] - y_elms[y_index];
4178 if (diff != 0) return Smi::FromInt(diff);
4179 }
4180
4181 // If one array is a suffix of the other array, the longest array is
4182 // the representation of the largest of the Smis in the
4183 // lexicographic ordering.
4184 return Smi::FromInt(x_index - y_index);
4185}
4186
4187
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004188static Object* Runtime_StringCompare(Arguments args) {
4189 NoHandleAllocation ha;
4190 ASSERT(args.length() == 2);
4191
4192 CONVERT_CHECKED(String, x, args[0]);
4193 CONVERT_CHECKED(String, y, args[1]);
4194
4195 // A few fast case tests before we flatten.
4196 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004197 if (y->length() == 0) {
4198 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004199 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004200 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004201 return Smi::FromInt(LESS);
4202 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004203
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004204 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004205 if (d < 0) return Smi::FromInt(LESS);
4206 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004207
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004208 x->TryFlattenIfNotFlat();
4209 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004210
4211 static StringInputBuffer bufx;
4212 static StringInputBuffer bufy;
4213 bufx.Reset(x);
4214 bufy.Reset(y);
4215 while (bufx.has_more() && bufy.has_more()) {
4216 int d = bufx.GetNext() - bufy.GetNext();
4217 if (d < 0) return Smi::FromInt(LESS);
4218 else if (d > 0) return Smi::FromInt(GREATER);
4219 }
4220
4221 // x is (non-trivial) prefix of y:
4222 if (bufy.has_more()) return Smi::FromInt(LESS);
4223 // y is prefix of x:
4224 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4225}
4226
4227
4228static Object* Runtime_Math_abs(Arguments args) {
4229 NoHandleAllocation ha;
4230 ASSERT(args.length() == 1);
4231
4232 CONVERT_DOUBLE_CHECKED(x, args[0]);
4233 return Heap::AllocateHeapNumber(fabs(x));
4234}
4235
4236
4237static Object* Runtime_Math_acos(Arguments args) {
4238 NoHandleAllocation ha;
4239 ASSERT(args.length() == 1);
4240
4241 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004242 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004243}
4244
4245
4246static Object* Runtime_Math_asin(Arguments args) {
4247 NoHandleAllocation ha;
4248 ASSERT(args.length() == 1);
4249
4250 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004251 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004252}
4253
4254
4255static Object* Runtime_Math_atan(Arguments args) {
4256 NoHandleAllocation ha;
4257 ASSERT(args.length() == 1);
4258
4259 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004260 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004261}
4262
4263
4264static Object* Runtime_Math_atan2(Arguments args) {
4265 NoHandleAllocation ha;
4266 ASSERT(args.length() == 2);
4267
4268 CONVERT_DOUBLE_CHECKED(x, args[0]);
4269 CONVERT_DOUBLE_CHECKED(y, args[1]);
4270 double result;
4271 if (isinf(x) && isinf(y)) {
4272 // Make sure that the result in case of two infinite arguments
4273 // is a multiple of Pi / 4. The sign of the result is determined
4274 // by the first argument (x) and the sign of the second argument
4275 // determines the multiplier: one or three.
4276 static double kPiDividedBy4 = 0.78539816339744830962;
4277 int multiplier = (x < 0) ? -1 : 1;
4278 if (y < 0) multiplier *= 3;
4279 result = multiplier * kPiDividedBy4;
4280 } else {
4281 result = atan2(x, y);
4282 }
4283 return Heap::AllocateHeapNumber(result);
4284}
4285
4286
4287static Object* Runtime_Math_ceil(Arguments args) {
4288 NoHandleAllocation ha;
4289 ASSERT(args.length() == 1);
4290
4291 CONVERT_DOUBLE_CHECKED(x, args[0]);
4292 return Heap::NumberFromDouble(ceiling(x));
4293}
4294
4295
4296static Object* Runtime_Math_cos(Arguments args) {
4297 NoHandleAllocation ha;
4298 ASSERT(args.length() == 1);
4299
4300 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004301 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004302}
4303
4304
4305static Object* Runtime_Math_exp(Arguments args) {
4306 NoHandleAllocation ha;
4307 ASSERT(args.length() == 1);
4308
4309 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004310 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004311}
4312
4313
4314static Object* Runtime_Math_floor(Arguments args) {
4315 NoHandleAllocation ha;
4316 ASSERT(args.length() == 1);
4317
4318 CONVERT_DOUBLE_CHECKED(x, args[0]);
4319 return Heap::NumberFromDouble(floor(x));
4320}
4321
4322
4323static Object* Runtime_Math_log(Arguments args) {
4324 NoHandleAllocation ha;
4325 ASSERT(args.length() == 1);
4326
4327 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004328 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004329}
4330
4331
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004332// Helper function to compute x^y, where y is known to be an
4333// integer. Uses binary decomposition to limit the number of
4334// multiplications; see the discussion in "Hacker's Delight" by Henry
4335// S. Warren, Jr., figure 11-6, page 213.
4336static double powi(double x, int y) {
4337 ASSERT(y != kMinInt);
4338 unsigned n = (y < 0) ? -y : y;
4339 double m = x;
4340 double p = 1;
4341 while (true) {
4342 if ((n & 1) != 0) p *= m;
4343 n >>= 1;
4344 if (n == 0) {
4345 if (y < 0) {
4346 // Unfortunately, we have to be careful when p has reached
4347 // infinity in the computation, because sometimes the higher
4348 // internal precision in the pow() implementation would have
4349 // given us a finite p. This happens very rarely.
4350 double result = 1.0 / p;
4351 return (result == 0 && isinf(p))
4352 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4353 : result;
4354 } else {
4355 return p;
4356 }
4357 }
4358 m *= m;
4359 }
4360}
4361
4362
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004363static Object* Runtime_Math_pow(Arguments args) {
4364 NoHandleAllocation ha;
4365 ASSERT(args.length() == 2);
4366
4367 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004368
4369 // If the second argument is a smi, it is much faster to call the
4370 // custom powi() function than the generic pow().
4371 if (args[1]->IsSmi()) {
4372 int y = Smi::cast(args[1])->value();
4373 return Heap::AllocateHeapNumber(powi(x, y));
4374 }
4375
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004376 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004377
4378 if (!isinf(x)) {
4379 if (y == 0.5) {
4380 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4381 // square root of a number. To speed up such computations, we
4382 // explictly check for this case and use the sqrt() function
4383 // which is faster than pow().
4384 return Heap::AllocateHeapNumber(sqrt(x));
4385 } else if (y == -0.5) {
4386 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4387 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4388 }
4389 }
4390
4391 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004392 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004393 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4394 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004395 } else {
4396 return Heap::AllocateHeapNumber(pow(x, y));
4397 }
4398}
4399
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004400
4401static Object* Runtime_Math_round(Arguments args) {
4402 NoHandleAllocation ha;
4403 ASSERT(args.length() == 1);
4404
4405 CONVERT_DOUBLE_CHECKED(x, args[0]);
4406 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4407 return Heap::NumberFromDouble(floor(x + 0.5));
4408}
4409
4410
4411static Object* Runtime_Math_sin(Arguments args) {
4412 NoHandleAllocation ha;
4413 ASSERT(args.length() == 1);
4414
4415 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004416 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004417}
4418
4419
4420static Object* Runtime_Math_sqrt(Arguments args) {
4421 NoHandleAllocation ha;
4422 ASSERT(args.length() == 1);
4423
4424 CONVERT_DOUBLE_CHECKED(x, args[0]);
4425 return Heap::AllocateHeapNumber(sqrt(x));
4426}
4427
4428
4429static Object* Runtime_Math_tan(Arguments args) {
4430 NoHandleAllocation ha;
4431 ASSERT(args.length() == 1);
4432
4433 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004434 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004435}
4436
4437
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004438// The NewArguments function is only used when constructing the
4439// arguments array when calling non-functions from JavaScript in
4440// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004441static Object* Runtime_NewArguments(Arguments args) {
4442 NoHandleAllocation ha;
4443 ASSERT(args.length() == 1);
4444
4445 // ECMA-262, 3rd., 10.1.8, p.39
4446 CONVERT_CHECKED(JSFunction, callee, args[0]);
4447
4448 // Compute the frame holding the arguments.
4449 JavaScriptFrameIterator it;
4450 it.AdvanceToArgumentsFrame();
4451 JavaScriptFrame* frame = it.frame();
4452
4453 const int length = frame->GetProvidedParametersCount();
4454 Object* result = Heap::AllocateArgumentsObject(callee, length);
4455 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004456 if (length > 0) {
4457 Object* obj = Heap::AllocateFixedArray(length);
4458 if (obj->IsFailure()) return obj;
4459 FixedArray* array = FixedArray::cast(obj);
4460 ASSERT(array->length() == length);
4461 WriteBarrierMode mode = array->GetWriteBarrierMode();
4462 for (int i = 0; i < length; i++) {
4463 array->set(i, frame->GetParameter(i), mode);
4464 }
4465 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004466 }
4467 return result;
4468}
4469
4470
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004471static Object* Runtime_NewArgumentsFast(Arguments args) {
4472 NoHandleAllocation ha;
4473 ASSERT(args.length() == 3);
4474
4475 JSFunction* callee = JSFunction::cast(args[0]);
4476 Object** parameters = reinterpret_cast<Object**>(args[1]);
4477 const int length = Smi::cast(args[2])->value();
4478
4479 Object* result = Heap::AllocateArgumentsObject(callee, length);
4480 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004481 // Allocate the elements if needed.
4482 if (length > 0) {
4483 // Allocate the fixed array.
4484 Object* obj = Heap::AllocateRawFixedArray(length);
4485 if (obj->IsFailure()) return obj;
4486 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4487 FixedArray* array = FixedArray::cast(obj);
4488 array->set_length(length);
4489 WriteBarrierMode mode = array->GetWriteBarrierMode();
4490 for (int i = 0; i < length; i++) {
4491 array->set(i, *--parameters, mode);
4492 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004493 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004494 }
4495 return result;
4496}
4497
4498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004499static Object* Runtime_NewClosure(Arguments args) {
4500 HandleScope scope;
4501 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00004502 CONVERT_ARG_CHECKED(Context, context, 0);
4503 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004504
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004505 PretenureFlag pretenure = (context->global_context() == *context)
4506 ? TENURED // Allocate global closures in old space.
4507 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004508 Handle<JSFunction> result =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004509 Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004510 return *result;
4511}
4512
4513
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004514static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004515 // TODO(385): Change this to create a construct stub specialized for
4516 // the given map to make allocation of simple objects - and maybe
4517 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004518 if (FLAG_inline_new
4519 && shared->has_only_simple_this_property_assignments()) {
4520 ConstructStubCompiler compiler;
4521 Object* code = compiler.CompileConstructStub(*shared);
4522 if (code->IsFailure()) {
4523 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4524 }
4525 return Code::cast(code);
4526 }
4527
4528 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004529}
4530
4531
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004532static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004533 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004534 ASSERT(args.length() == 1);
4535
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004536 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004537
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004538 // If the constructor isn't a proper function we throw a type error.
4539 if (!constructor->IsJSFunction()) {
4540 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4541 Handle<Object> type_error =
4542 Factory::NewTypeError("not_constructor", arguments);
4543 return Top::Throw(*type_error);
4544 }
4545
4546 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004547#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004548 // Handle stepping into constructors if step into is active.
4549 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004550 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004551 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004552#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004553
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004554 if (function->has_initial_map()) {
4555 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004556 // The 'Function' function ignores the receiver object when
4557 // called using 'new' and creates a new JSFunction object that
4558 // is returned. The receiver object is only used for error
4559 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004560 // JSFunction. Factory::NewJSObject() should not be used to
4561 // allocate JSFunctions since it does not properly initialize
4562 // the shared part of the function. Since the receiver is
4563 // ignored anyway, we use the global object as the receiver
4564 // instead of a new JSFunction object. This way, errors are
4565 // reported the same way whether or not 'Function' is called
4566 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004567 return Top::context()->global();
4568 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004569 }
4570
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004571 // The function should be compiled for the optimization hints to be available.
4572 if (!function->shared()->is_compiled()) {
4573 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4574 CLEAR_EXCEPTION,
4575 0);
4576 }
4577
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004578 bool first_allocation = !function->has_initial_map();
4579 Handle<JSObject> result = Factory::NewJSObject(function);
4580 if (first_allocation) {
4581 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004582 Handle<Code> stub = Handle<Code>(
4583 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004584 function->shared()->set_construct_stub(*stub);
4585 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004586
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004587 Counters::constructed_objects.Increment();
4588 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004589
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004590 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591}
4592
4593
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004594static Object* Runtime_LazyCompile(Arguments args) {
4595 HandleScope scope;
4596 ASSERT(args.length() == 1);
4597
4598 Handle<JSFunction> function = args.at<JSFunction>(0);
4599#ifdef DEBUG
4600 if (FLAG_trace_lazy) {
4601 PrintF("[lazy: ");
4602 function->shared()->name()->Print();
4603 PrintF("]\n");
4604 }
4605#endif
4606
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004607 // Compile the target function. Here we compile using CompileLazyInLoop in
4608 // order to get the optimized version. This helps code like delta-blue
4609 // that calls performance-critical routines through constructors. A
4610 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4611 // direct call. Since the in-loop tracking takes place through CallICs
4612 // this means that things called through constructors are never known to
4613 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004614 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004615 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004616 return Failure::Exception();
4617 }
4618
4619 return function->code();
4620}
4621
4622
4623static Object* Runtime_GetCalledFunction(Arguments args) {
4624 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004625 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004626 StackFrameIterator it;
4627 // Get past the JS-to-C exit frame.
4628 ASSERT(it.frame()->is_exit());
4629 it.Advance();
4630 // Get past the CALL_NON_FUNCTION activation frame.
4631 ASSERT(it.frame()->is_java_script());
4632 it.Advance();
4633 // Argument adaptor frames do not copy the function; we have to skip
4634 // past them to get to the real calling frame.
4635 if (it.frame()->is_arguments_adaptor()) it.Advance();
4636 // Get the function from the top of the expression stack of the
4637 // calling frame.
4638 StandardFrame* frame = StandardFrame::cast(it.frame());
4639 int index = frame->ComputeExpressionsCount() - 1;
4640 Object* result = frame->GetExpression(index);
4641 return result;
4642}
4643
4644
4645static Object* Runtime_GetFunctionDelegate(Arguments args) {
4646 HandleScope scope;
4647 ASSERT(args.length() == 1);
4648 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4649 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4650}
4651
4652
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004653static Object* Runtime_GetConstructorDelegate(Arguments args) {
4654 HandleScope scope;
4655 ASSERT(args.length() == 1);
4656 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4657 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4658}
4659
4660
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004661static Object* Runtime_NewContext(Arguments args) {
4662 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004663 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004664
kasper.lund7276f142008-07-30 08:49:36 +00004665 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4667 Object* result = Heap::AllocateFunctionContext(length, function);
4668 if (result->IsFailure()) return result;
4669
4670 Top::set_context(Context::cast(result));
4671
kasper.lund7276f142008-07-30 08:49:36 +00004672 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004673}
4674
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004675static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004676 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004677 Object* js_object = object;
4678 if (!js_object->IsJSObject()) {
4679 js_object = js_object->ToObject();
4680 if (js_object->IsFailure()) {
4681 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004682 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004683 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004684 Handle<Object> result =
4685 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4686 return Top::Throw(*result);
4687 }
4688 }
4689
4690 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004691 Heap::AllocateWithContext(Top::context(),
4692 JSObject::cast(js_object),
4693 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004694 if (result->IsFailure()) return result;
4695
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004696 Context* context = Context::cast(result);
4697 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004698
kasper.lund7276f142008-07-30 08:49:36 +00004699 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004700}
4701
4702
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004703static Object* Runtime_PushContext(Arguments args) {
4704 NoHandleAllocation ha;
4705 ASSERT(args.length() == 1);
4706 return PushContextHelper(args[0], false);
4707}
4708
4709
4710static Object* Runtime_PushCatchContext(Arguments args) {
4711 NoHandleAllocation ha;
4712 ASSERT(args.length() == 1);
4713 return PushContextHelper(args[0], true);
4714}
4715
4716
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004717static Object* Runtime_LookupContext(Arguments args) {
4718 HandleScope scope;
4719 ASSERT(args.length() == 2);
4720
4721 CONVERT_ARG_CHECKED(Context, context, 0);
4722 CONVERT_ARG_CHECKED(String, name, 1);
4723
4724 int index;
4725 PropertyAttributes attributes;
4726 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004727 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004728 context->Lookup(name, flags, &index, &attributes);
4729
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004730 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004731 ASSERT(holder->IsJSObject());
4732 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004733 }
4734
4735 // No intermediate context found. Use global object by default.
4736 return Top::context()->global();
4737}
4738
4739
ager@chromium.orga1645e22009-09-09 19:27:10 +00004740// A mechanism to return a pair of Object pointers in registers (if possible).
4741// How this is achieved is calling convention-dependent.
4742// All currently supported x86 compiles uses calling conventions that are cdecl
4743// variants where a 64-bit value is returned in two 32-bit registers
4744// (edx:eax on ia32, r1:r0 on ARM).
4745// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4746// In Win64 calling convention, a struct of two pointers is returned in memory,
4747// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004748#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004749struct ObjectPair {
4750 Object* x;
4751 Object* y;
4752};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004753
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004754static inline ObjectPair MakePair(Object* x, Object* y) {
4755 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004756 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4757 // In Win64 they are assigned to a hidden first argument.
4758 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004759}
4760#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004761typedef uint64_t ObjectPair;
4762static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004763 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004764 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004766#endif
4767
4768
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004769static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004770 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4771 USE(attributes);
4772 return x->IsTheHole() ? Heap::undefined_value() : x;
4773}
4774
4775
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004776static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4777 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004778 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004779 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004780 JSFunction* context_extension_function =
4781 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004782 // If the holder isn't a context extension object, we just return it
4783 // as the receiver. This allows arguments objects to be used as
4784 // receivers, but only if they are put in the context scope chain
4785 // explicitly via a with-statement.
4786 Object* constructor = holder->map()->constructor();
4787 if (constructor != context_extension_function) return holder;
4788 // Fall back to using the global object as the receiver if the
4789 // property turns out to be a local variable allocated in a context
4790 // extension object - introduced via eval.
4791 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004792}
4793
4794
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004795static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004796 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004797 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004798
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004799 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004800 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004801 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004803 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004804
4805 int index;
4806 PropertyAttributes attributes;
4807 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004808 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004809 context->Lookup(name, flags, &index, &attributes);
4810
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004811 // If the index is non-negative, the slot has been found in a local
4812 // variable or a parameter. Read it from the context object or the
4813 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004814 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004815 // If the "property" we were looking for is a local variable or an
4816 // argument in a context, the receiver is the global object; see
4817 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4818 JSObject* receiver = Top::context()->global()->global_receiver();
4819 Object* value = (holder->IsContext())
4820 ? Context::cast(*holder)->get(index)
4821 : JSObject::cast(*holder)->GetElement(index);
4822 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004823 }
4824
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004825 // If the holder is found, we read the property from it.
4826 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004827 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004828 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004829 JSObject* receiver;
4830 if (object->IsGlobalObject()) {
4831 receiver = GlobalObject::cast(object)->global_receiver();
4832 } else if (context->is_exception_holder(*holder)) {
4833 receiver = Top::context()->global()->global_receiver();
4834 } else {
4835 receiver = ComputeReceiverForNonGlobal(object);
4836 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004837 // No need to unhole the value here. This is taken care of by the
4838 // GetProperty function.
4839 Object* value = object->GetProperty(*name);
4840 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004841 }
4842
4843 if (throw_error) {
4844 // The property doesn't exist - throw exception.
4845 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004846 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004847 return MakePair(Top::Throw(*reference_error), NULL);
4848 } else {
4849 // The property doesn't exist - return undefined
4850 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4851 }
4852}
4853
4854
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004855static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004856 return LoadContextSlotHelper(args, true);
4857}
4858
4859
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004860static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004861 return LoadContextSlotHelper(args, false);
4862}
4863
4864
4865static Object* Runtime_StoreContextSlot(Arguments args) {
4866 HandleScope scope;
4867 ASSERT(args.length() == 3);
4868
4869 Handle<Object> value(args[0]);
4870 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004871 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004872
4873 int index;
4874 PropertyAttributes attributes;
4875 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004876 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004877 context->Lookup(name, flags, &index, &attributes);
4878
4879 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004880 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004881 // Ignore if read_only variable.
4882 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004883 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004884 }
4885 } else {
4886 ASSERT((attributes & READ_ONLY) == 0);
4887 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004888 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004889 USE(result);
4890 ASSERT(!result->IsFailure());
4891 }
4892 return *value;
4893 }
4894
4895 // Slow case: The property is not in a FixedArray context.
4896 // It is either in an JSObject extension context or it was not found.
4897 Handle<JSObject> context_ext;
4898
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004899 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004900 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004901 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004902 } else {
4903 // The property was not found. It needs to be stored in the global context.
4904 ASSERT(attributes == ABSENT);
4905 attributes = NONE;
4906 context_ext = Handle<JSObject>(Top::context()->global());
4907 }
4908
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004909 // Set the property, but ignore if read_only variable on the context
4910 // extension object itself.
4911 if ((attributes & READ_ONLY) == 0 ||
4912 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004913 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4914 if (set.is_null()) {
4915 // Failure::Exception is converted to a null handle in the
4916 // handle-based methods such as SetProperty. We therefore need
4917 // to convert null handles back to exceptions.
4918 ASSERT(Top::has_pending_exception());
4919 return Failure::Exception();
4920 }
4921 }
4922 return *value;
4923}
4924
4925
4926static Object* Runtime_Throw(Arguments args) {
4927 HandleScope scope;
4928 ASSERT(args.length() == 1);
4929
4930 return Top::Throw(args[0]);
4931}
4932
4933
4934static Object* Runtime_ReThrow(Arguments args) {
4935 HandleScope scope;
4936 ASSERT(args.length() == 1);
4937
4938 return Top::ReThrow(args[0]);
4939}
4940
4941
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004942static Object* Runtime_PromoteScheduledException(Arguments args) {
4943 ASSERT_EQ(0, args.length());
4944 return Top::PromoteScheduledException();
4945}
4946
4947
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004948static Object* Runtime_ThrowReferenceError(Arguments args) {
4949 HandleScope scope;
4950 ASSERT(args.length() == 1);
4951
4952 Handle<Object> name(args[0]);
4953 Handle<Object> reference_error =
4954 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4955 return Top::Throw(*reference_error);
4956}
4957
4958
4959static Object* Runtime_StackOverflow(Arguments args) {
4960 NoHandleAllocation na;
4961 return Top::StackOverflow();
4962}
4963
4964
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004965static Object* Runtime_StackGuard(Arguments args) {
4966 ASSERT(args.length() == 1);
4967
4968 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004969 if (StackGuard::IsStackOverflow()) {
4970 return Runtime_StackOverflow(args);
4971 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004972
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004973 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004974}
4975
4976
4977// NOTE: These PrintXXX functions are defined for all builds (not just
4978// DEBUG builds) because we may want to be able to trace function
4979// calls in all modes.
4980static void PrintString(String* str) {
4981 // not uncommon to have empty strings
4982 if (str->length() > 0) {
4983 SmartPointer<char> s =
4984 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4985 PrintF("%s", *s);
4986 }
4987}
4988
4989
4990static void PrintObject(Object* obj) {
4991 if (obj->IsSmi()) {
4992 PrintF("%d", Smi::cast(obj)->value());
4993 } else if (obj->IsString() || obj->IsSymbol()) {
4994 PrintString(String::cast(obj));
4995 } else if (obj->IsNumber()) {
4996 PrintF("%g", obj->Number());
4997 } else if (obj->IsFailure()) {
4998 PrintF("<failure>");
4999 } else if (obj->IsUndefined()) {
5000 PrintF("<undefined>");
5001 } else if (obj->IsNull()) {
5002 PrintF("<null>");
5003 } else if (obj->IsTrue()) {
5004 PrintF("<true>");
5005 } else if (obj->IsFalse()) {
5006 PrintF("<false>");
5007 } else {
5008 PrintF("%p", obj);
5009 }
5010}
5011
5012
5013static int StackSize() {
5014 int n = 0;
5015 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
5016 return n;
5017}
5018
5019
5020static void PrintTransition(Object* result) {
5021 // indentation
5022 { const int nmax = 80;
5023 int n = StackSize();
5024 if (n <= nmax)
5025 PrintF("%4d:%*s", n, n, "");
5026 else
5027 PrintF("%4d:%*s", n, nmax, "...");
5028 }
5029
5030 if (result == NULL) {
5031 // constructor calls
5032 JavaScriptFrameIterator it;
5033 JavaScriptFrame* frame = it.frame();
5034 if (frame->IsConstructor()) PrintF("new ");
5035 // function name
5036 Object* fun = frame->function();
5037 if (fun->IsJSFunction()) {
5038 PrintObject(JSFunction::cast(fun)->shared()->name());
5039 } else {
5040 PrintObject(fun);
5041 }
5042 // function arguments
5043 // (we are intentionally only printing the actually
5044 // supplied parameters, not all parameters required)
5045 PrintF("(this=");
5046 PrintObject(frame->receiver());
5047 const int length = frame->GetProvidedParametersCount();
5048 for (int i = 0; i < length; i++) {
5049 PrintF(", ");
5050 PrintObject(frame->GetParameter(i));
5051 }
5052 PrintF(") {\n");
5053
5054 } else {
5055 // function result
5056 PrintF("} -> ");
5057 PrintObject(result);
5058 PrintF("\n");
5059 }
5060}
5061
5062
5063static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005064 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005065 NoHandleAllocation ha;
5066 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005067 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005068}
5069
5070
5071static Object* Runtime_TraceExit(Arguments args) {
5072 NoHandleAllocation ha;
5073 PrintTransition(args[0]);
5074 return args[0]; // return TOS
5075}
5076
5077
5078static Object* Runtime_DebugPrint(Arguments args) {
5079 NoHandleAllocation ha;
5080 ASSERT(args.length() == 1);
5081
5082#ifdef DEBUG
5083 if (args[0]->IsString()) {
5084 // If we have a string, assume it's a code "marker"
5085 // and print some interesting cpu debugging info.
5086 JavaScriptFrameIterator it;
5087 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005088 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
5089 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005090 } else {
5091 PrintF("DebugPrint: ");
5092 }
5093 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005094 if (args[0]->IsHeapObject()) {
5095 HeapObject::cast(args[0])->map()->Print();
5096 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005097#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005098 // ShortPrint is available in release mode. Print is not.
5099 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005100#endif
5101 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00005102 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005103
5104 return args[0]; // return TOS
5105}
5106
5107
5108static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005109 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005110 NoHandleAllocation ha;
5111 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005112 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005113}
5114
5115
mads.s.ager31e71382008-08-13 09:32:07 +00005116static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005117 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005118 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005119
5120 // According to ECMA-262, section 15.9.1, page 117, the precision of
5121 // the number in a Date object representing a particular instant in
5122 // time is milliseconds. Therefore, we floor the result of getting
5123 // the OS time.
5124 double millis = floor(OS::TimeCurrentMillis());
5125 return Heap::NumberFromDouble(millis);
5126}
5127
5128
5129static Object* Runtime_DateParseString(Arguments args) {
5130 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005131 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005132
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005133 CONVERT_ARG_CHECKED(String, str, 0);
5134 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005135
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005136 CONVERT_ARG_CHECKED(JSArray, output, 1);
5137 RUNTIME_ASSERT(output->HasFastElements());
5138
5139 AssertNoAllocation no_allocation;
5140
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005141 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005142 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5143 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005144 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005145 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005146 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005147 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005148 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5149 }
5150
5151 if (result) {
5152 return *output;
5153 } else {
5154 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005155 }
5156}
5157
5158
5159static Object* Runtime_DateLocalTimezone(Arguments args) {
5160 NoHandleAllocation ha;
5161 ASSERT(args.length() == 1);
5162
5163 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005164 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005165 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5166}
5167
5168
5169static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5170 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005171 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005172
5173 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5174}
5175
5176
5177static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5178 NoHandleAllocation ha;
5179 ASSERT(args.length() == 1);
5180
5181 CONVERT_DOUBLE_CHECKED(x, args[0]);
5182 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5183}
5184
5185
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005186static Object* Runtime_NumberIsFinite(Arguments args) {
5187 NoHandleAllocation ha;
5188 ASSERT(args.length() == 1);
5189
5190 CONVERT_DOUBLE_CHECKED(value, args[0]);
5191 Object* result;
5192 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5193 result = Heap::false_value();
5194 } else {
5195 result = Heap::true_value();
5196 }
5197 return result;
5198}
5199
5200
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005201static Object* Runtime_GlobalReceiver(Arguments args) {
5202 ASSERT(args.length() == 1);
5203 Object* global = args[0];
5204 if (!global->IsJSGlobalObject()) return Heap::null_value();
5205 return JSGlobalObject::cast(global)->global_receiver();
5206}
5207
5208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005209static Object* Runtime_CompileString(Arguments args) {
5210 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005211 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005212 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005213 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005214
ager@chromium.org381abbb2009-02-25 13:23:22 +00005215 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005216 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005217 Compiler::ValidationState validate = (is_json->IsTrue())
5218 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005219 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5220 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005221 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005222 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005223 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005224 Handle<JSFunction> fun =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005225 Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005226 return *fun;
5227}
5228
5229
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005230static Handle<JSFunction> GetBuiltinFunction(String* name) {
5231 LookupResult result;
5232 Top::global_context()->builtins()->LocalLookup(name, &result);
5233 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5234}
5235
5236
5237static Object* CompileDirectEval(Handle<String> source) {
5238 // Compute the eval context.
5239 HandleScope scope;
5240 StackFrameLocator locator;
5241 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5242 Handle<Context> context(Context::cast(frame->context()));
5243 bool is_global = context->IsGlobalContext();
5244
ager@chromium.org381abbb2009-02-25 13:23:22 +00005245 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005246 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5247 source,
5248 context,
5249 is_global,
5250 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005251 if (boilerplate.is_null()) return Failure::Exception();
5252 Handle<JSFunction> fun =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005253 Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005254 return *fun;
5255}
5256
5257
5258static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5259 ASSERT(args.length() == 2);
5260
5261 HandleScope scope;
5262
5263 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5264
5265 Handle<Object> receiver;
5266
5267 // Find where the 'eval' symbol is bound. It is unaliased only if
5268 // it is bound in the global context.
5269 StackFrameLocator locator;
5270 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5271 Handle<Context> context(Context::cast(frame->context()));
5272 int index;
5273 PropertyAttributes attributes;
5274 while (!context.is_null()) {
5275 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5276 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005277 // Stop search when eval is found or when the global context is
5278 // reached.
5279 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005280 if (context->is_function_context()) {
5281 context = Handle<Context>(Context::cast(context->closure()->context()));
5282 } else {
5283 context = Handle<Context>(context->previous());
5284 }
5285 }
5286
iposva@chromium.org245aa852009-02-10 00:49:54 +00005287 // If eval could not be resolved, it has been deleted and we need to
5288 // throw a reference error.
5289 if (attributes == ABSENT) {
5290 Handle<Object> name = Factory::eval_symbol();
5291 Handle<Object> reference_error =
5292 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5293 return Top::Throw(*reference_error);
5294 }
5295
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005296 if (context->IsGlobalContext()) {
5297 // 'eval' is bound in the global context, but it may have been overwritten.
5298 // Compare it to the builtin 'GlobalEval' function to make sure.
5299 Handle<JSFunction> global_eval =
5300 GetBuiltinFunction(Heap::global_eval_symbol());
5301 if (global_eval.is_identical_to(callee)) {
5302 // A direct eval call.
5303 if (args[1]->IsString()) {
5304 CONVERT_ARG_CHECKED(String, source, 1);
5305 // A normal eval call on a string. Compile it and return the
5306 // compiled function bound in the local context.
5307 Object* compiled_source = CompileDirectEval(source);
5308 if (compiled_source->IsFailure()) return compiled_source;
5309 receiver = Handle<Object>(frame->receiver());
5310 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5311 } else {
5312 // An eval call that is not called on a string. Global eval
5313 // deals better with this.
5314 receiver = Handle<Object>(Top::global_context()->global());
5315 }
5316 } else {
5317 // 'eval' is overwritten. Just call the function with the given arguments.
5318 receiver = Handle<Object>(Top::global_context()->global());
5319 }
5320 } else {
5321 // 'eval' is not bound in the global context. Just call the function
5322 // with the given arguments. This is not necessarily the global eval.
5323 if (receiver->IsContext()) {
5324 context = Handle<Context>::cast(receiver);
5325 receiver = Handle<Object>(context->get(index));
5326 }
5327 }
5328
5329 Handle<FixedArray> call = Factory::NewFixedArray(2);
5330 call->set(0, *callee);
5331 call->set(1, *receiver);
5332 return *call;
5333}
5334
5335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005336static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5337 // This utility adjusts the property attributes for newly created Function
5338 // object ("new Function(...)") by changing the map.
5339 // All it does is changing the prototype property to enumerable
5340 // as specified in ECMA262, 15.3.5.2.
5341 HandleScope scope;
5342 ASSERT(args.length() == 1);
5343 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5344 ASSERT(func->map()->instance_type() ==
5345 Top::function_instance_map()->instance_type());
5346 ASSERT(func->map()->instance_size() ==
5347 Top::function_instance_map()->instance_size());
5348 func->set_map(*Top::function_instance_map());
5349 return *func;
5350}
5351
5352
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005353// Push an array unto an array of arrays if it is not already in the
5354// array. Returns true if the element was pushed on the stack and
5355// false otherwise.
5356static Object* Runtime_PushIfAbsent(Arguments args) {
5357 ASSERT(args.length() == 2);
5358 CONVERT_CHECKED(JSArray, array, args[0]);
5359 CONVERT_CHECKED(JSArray, element, args[1]);
5360 RUNTIME_ASSERT(array->HasFastElements());
5361 int length = Smi::cast(array->length())->value();
5362 FixedArray* elements = FixedArray::cast(array->elements());
5363 for (int i = 0; i < length; i++) {
5364 if (elements->get(i) == element) return Heap::false_value();
5365 }
5366 Object* obj = array->SetFastElement(length, element);
5367 if (obj->IsFailure()) return obj;
5368 return Heap::true_value();
5369}
5370
5371
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005372/**
5373 * A simple visitor visits every element of Array's.
5374 * The backend storage can be a fixed array for fast elements case,
5375 * or a dictionary for sparse array. Since Dictionary is a subtype
5376 * of FixedArray, the class can be used by both fast and slow cases.
5377 * The second parameter of the constructor, fast_elements, specifies
5378 * whether the storage is a FixedArray or Dictionary.
5379 *
5380 * An index limit is used to deal with the situation that a result array
5381 * length overflows 32-bit non-negative integer.
5382 */
5383class ArrayConcatVisitor {
5384 public:
5385 ArrayConcatVisitor(Handle<FixedArray> storage,
5386 uint32_t index_limit,
5387 bool fast_elements) :
5388 storage_(storage), index_limit_(index_limit),
5389 fast_elements_(fast_elements), index_offset_(0) { }
5390
5391 void visit(uint32_t i, Handle<Object> elm) {
5392 uint32_t index = i + index_offset_;
5393 if (index >= index_limit_) return;
5394
5395 if (fast_elements_) {
5396 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5397 storage_->set(index, *elm);
5398
5399 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005400 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5401 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005402 Factory::DictionaryAtNumberPut(dict, index, elm);
5403 if (!result.is_identical_to(dict))
5404 storage_ = result;
5405 }
5406 }
5407
5408 void increase_index_offset(uint32_t delta) {
5409 index_offset_ += delta;
5410 }
5411
5412 private:
5413 Handle<FixedArray> storage_;
5414 uint32_t index_limit_;
5415 bool fast_elements_;
5416 uint32_t index_offset_;
5417};
5418
5419
ager@chromium.org3811b432009-10-28 14:53:37 +00005420template<class ExternalArrayClass, class ElementType>
5421static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5422 bool elements_are_ints,
5423 bool elements_are_guaranteed_smis,
5424 uint32_t range,
5425 ArrayConcatVisitor* visitor) {
5426 Handle<ExternalArrayClass> array(
5427 ExternalArrayClass::cast(receiver->elements()));
5428 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5429
5430 if (visitor != NULL) {
5431 if (elements_are_ints) {
5432 if (elements_are_guaranteed_smis) {
5433 for (uint32_t j = 0; j < len; j++) {
5434 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5435 visitor->visit(j, e);
5436 }
5437 } else {
5438 for (uint32_t j = 0; j < len; j++) {
5439 int64_t val = static_cast<int64_t>(array->get(j));
5440 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5441 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5442 visitor->visit(j, e);
5443 } else {
5444 Handle<Object> e(
5445 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5446 visitor->visit(j, e);
5447 }
5448 }
5449 }
5450 } else {
5451 for (uint32_t j = 0; j < len; j++) {
5452 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5453 visitor->visit(j, e);
5454 }
5455 }
5456 }
5457
5458 return len;
5459}
5460
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005461/**
5462 * A helper function that visits elements of a JSObject. Only elements
5463 * whose index between 0 and range (exclusive) are visited.
5464 *
5465 * If the third parameter, visitor, is not NULL, the visitor is called
5466 * with parameters, 'visitor_index_offset + element index' and the element.
5467 *
5468 * It returns the number of visisted elements.
5469 */
5470static uint32_t IterateElements(Handle<JSObject> receiver,
5471 uint32_t range,
5472 ArrayConcatVisitor* visitor) {
5473 uint32_t num_of_elements = 0;
5474
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005475 switch (receiver->GetElementsKind()) {
5476 case JSObject::FAST_ELEMENTS: {
5477 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5478 uint32_t len = elements->length();
5479 if (range < len) {
5480 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005481 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005482
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005483 for (uint32_t j = 0; j < len; j++) {
5484 Handle<Object> e(elements->get(j));
5485 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005486 num_of_elements++;
5487 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005488 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005489 }
5490 }
5491 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005492 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005493 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005494 case JSObject::PIXEL_ELEMENTS: {
5495 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5496 uint32_t len = pixels->length();
5497 if (range < len) {
5498 len = range;
5499 }
5500
5501 for (uint32_t j = 0; j < len; j++) {
5502 num_of_elements++;
5503 if (visitor != NULL) {
5504 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5505 visitor->visit(j, e);
5506 }
5507 }
5508 break;
5509 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005510 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5511 num_of_elements =
5512 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5513 receiver, true, true, range, visitor);
5514 break;
5515 }
5516 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5517 num_of_elements =
5518 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5519 receiver, true, true, range, visitor);
5520 break;
5521 }
5522 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5523 num_of_elements =
5524 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5525 receiver, true, true, range, visitor);
5526 break;
5527 }
5528 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5529 num_of_elements =
5530 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5531 receiver, true, true, range, visitor);
5532 break;
5533 }
5534 case JSObject::EXTERNAL_INT_ELEMENTS: {
5535 num_of_elements =
5536 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5537 receiver, true, false, range, visitor);
5538 break;
5539 }
5540 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5541 num_of_elements =
5542 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5543 receiver, true, false, range, visitor);
5544 break;
5545 }
5546 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5547 num_of_elements =
5548 IterateExternalArrayElements<ExternalFloatArray, float>(
5549 receiver, false, false, range, visitor);
5550 break;
5551 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005552 case JSObject::DICTIONARY_ELEMENTS: {
5553 Handle<NumberDictionary> dict(receiver->element_dictionary());
5554 uint32_t capacity = dict->Capacity();
5555 for (uint32_t j = 0; j < capacity; j++) {
5556 Handle<Object> k(dict->KeyAt(j));
5557 if (dict->IsKey(*k)) {
5558 ASSERT(k->IsNumber());
5559 uint32_t index = static_cast<uint32_t>(k->Number());
5560 if (index < range) {
5561 num_of_elements++;
5562 if (visitor) {
5563 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5564 }
5565 }
5566 }
5567 }
5568 break;
5569 }
5570 default:
5571 UNREACHABLE();
5572 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005573 }
5574
5575 return num_of_elements;
5576}
5577
5578
5579/**
5580 * A helper function that visits elements of an Array object, and elements
5581 * on its prototypes.
5582 *
5583 * Elements on prototypes are visited first, and only elements whose indices
5584 * less than Array length are visited.
5585 *
5586 * If a ArrayConcatVisitor object is given, the visitor is called with
5587 * parameters, element's index + visitor_index_offset and the element.
5588 */
5589static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5590 ArrayConcatVisitor* visitor) {
5591 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5592 Handle<Object> obj = array;
5593
5594 static const int kEstimatedPrototypes = 3;
5595 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5596
5597 // Visit prototype first. If an element on the prototype is shadowed by
5598 // the inheritor using the same index, the ArrayConcatVisitor visits
5599 // the prototype element before the shadowing element.
5600 // The visitor can simply overwrite the old value by new value using
5601 // the same index. This follows Array::concat semantics.
5602 while (!obj->IsNull()) {
5603 objects.Add(Handle<JSObject>::cast(obj));
5604 obj = Handle<Object>(obj->GetPrototype());
5605 }
5606
5607 uint32_t nof_elements = 0;
5608 for (int i = objects.length() - 1; i >= 0; i--) {
5609 Handle<JSObject> obj = objects[i];
5610 nof_elements +=
5611 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5612 }
5613
5614 return nof_elements;
5615}
5616
5617
5618/**
5619 * A helper function of Runtime_ArrayConcat.
5620 *
5621 * The first argument is an Array of arrays and objects. It is the
5622 * same as the arguments array of Array::concat JS function.
5623 *
5624 * If an argument is an Array object, the function visits array
5625 * elements. If an argument is not an Array object, the function
5626 * visits the object as if it is an one-element array.
5627 *
5628 * If the result array index overflows 32-bit integer, the rounded
5629 * non-negative number is used as new length. For example, if one
5630 * array length is 2^32 - 1, second array length is 1, the
5631 * concatenated array length is 0.
5632 */
5633static uint32_t IterateArguments(Handle<JSArray> arguments,
5634 ArrayConcatVisitor* visitor) {
5635 uint32_t visited_elements = 0;
5636 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5637
5638 for (uint32_t i = 0; i < num_of_args; i++) {
5639 Handle<Object> obj(arguments->GetElement(i));
5640 if (obj->IsJSArray()) {
5641 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5642 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5643 uint32_t nof_elements =
5644 IterateArrayAndPrototypeElements(array, visitor);
5645 // Total elements of array and its prototype chain can be more than
5646 // the array length, but ArrayConcat can only concatenate at most
5647 // the array length number of elements.
5648 visited_elements += (nof_elements > len) ? len : nof_elements;
5649 if (visitor) visitor->increase_index_offset(len);
5650
5651 } else {
5652 if (visitor) {
5653 visitor->visit(0, obj);
5654 visitor->increase_index_offset(1);
5655 }
5656 visited_elements++;
5657 }
5658 }
5659 return visited_elements;
5660}
5661
5662
5663/**
5664 * Array::concat implementation.
5665 * See ECMAScript 262, 15.4.4.4.
5666 */
5667static Object* Runtime_ArrayConcat(Arguments args) {
5668 ASSERT(args.length() == 1);
5669 HandleScope handle_scope;
5670
5671 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5672 Handle<JSArray> arguments(arg_arrays);
5673
5674 // Pass 1: estimate the number of elements of the result
5675 // (it could be more than real numbers if prototype has elements).
5676 uint32_t result_length = 0;
5677 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5678
5679 { AssertNoAllocation nogc;
5680 for (uint32_t i = 0; i < num_of_args; i++) {
5681 Object* obj = arguments->GetElement(i);
5682 if (obj->IsJSArray()) {
5683 result_length +=
5684 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5685 } else {
5686 result_length++;
5687 }
5688 }
5689 }
5690
5691 // Allocate an empty array, will set length and content later.
5692 Handle<JSArray> result = Factory::NewJSArray(0);
5693
5694 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5695 // If estimated number of elements is more than half of length, a
5696 // fixed array (fast case) is more time and space-efficient than a
5697 // dictionary.
5698 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5699
5700 Handle<FixedArray> storage;
5701 if (fast_case) {
5702 // The backing storage array must have non-existing elements to
5703 // preserve holes across concat operations.
5704 storage = Factory::NewFixedArrayWithHoles(result_length);
5705
5706 } else {
5707 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5708 uint32_t at_least_space_for = estimate_nof_elements +
5709 (estimate_nof_elements >> 2);
5710 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005711 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005712 }
5713
5714 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5715
5716 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5717
5718 IterateArguments(arguments, &visitor);
5719
5720 result->set_length(*len);
5721 result->set_elements(*storage);
5722
5723 return *result;
5724}
5725
5726
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005727// This will not allocate (flatten the string), but it may run
5728// very slowly for very deeply nested ConsStrings. For debugging use only.
5729static Object* Runtime_GlobalPrint(Arguments args) {
5730 NoHandleAllocation ha;
5731 ASSERT(args.length() == 1);
5732
5733 CONVERT_CHECKED(String, string, args[0]);
5734 StringInputBuffer buffer(string);
5735 while (buffer.has_more()) {
5736 uint16_t character = buffer.GetNext();
5737 PrintF("%c", character);
5738 }
5739 return string;
5740}
5741
ager@chromium.org5ec48922009-05-05 07:25:34 +00005742// Moves all own elements of an object, that are below a limit, to positions
5743// starting at zero. All undefined values are placed after non-undefined values,
5744// and are followed by non-existing element. Does not change the length
5745// property.
5746// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005747static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005748 ASSERT(args.length() == 2);
5749 CONVERT_CHECKED(JSObject, object, args[0]);
5750 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5751 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005752}
5753
5754
5755// Move contents of argument 0 (an array) to argument 1 (an array)
5756static Object* Runtime_MoveArrayContents(Arguments args) {
5757 ASSERT(args.length() == 2);
5758 CONVERT_CHECKED(JSArray, from, args[0]);
5759 CONVERT_CHECKED(JSArray, to, args[1]);
5760 to->SetContent(FixedArray::cast(from->elements()));
5761 to->set_length(from->length());
5762 from->SetContent(Heap::empty_fixed_array());
5763 from->set_length(0);
5764 return to;
5765}
5766
5767
5768// How many elements does this array have?
5769static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5770 ASSERT(args.length() == 1);
5771 CONVERT_CHECKED(JSArray, array, args[0]);
5772 HeapObject* elements = array->elements();
5773 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005774 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005775 } else {
5776 return array->length();
5777 }
5778}
5779
5780
5781// Returns an array that tells you where in the [0, length) interval an array
5782// might have elements. Can either return keys or intervals. Keys can have
5783// gaps in (undefined). Intervals can also span over some undefined keys.
5784static Object* Runtime_GetArrayKeys(Arguments args) {
5785 ASSERT(args.length() == 2);
5786 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005787 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005788 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005789 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005790 // Create an array and get all the keys into it, then remove all the
5791 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005792 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793 int keys_length = keys->length();
5794 for (int i = 0; i < keys_length; i++) {
5795 Object* key = keys->get(i);
5796 uint32_t index;
5797 if (!Array::IndexFromObject(key, &index) || index >= length) {
5798 // Zap invalid keys.
5799 keys->set_undefined(i);
5800 }
5801 }
5802 return *Factory::NewJSArrayWithElements(keys);
5803 } else {
5804 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5805 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005806 single_interval->set(0,
5807 Smi::FromInt(-1),
5808 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005809 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5810 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005811 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005812 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005813 single_interval->set(1, *length_object);
5814 return *Factory::NewJSArrayWithElements(single_interval);
5815 }
5816}
5817
5818
5819// DefineAccessor takes an optional final argument which is the
5820// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5821// to the way accessors are implemented, it is set for both the getter
5822// and setter on the first call to DefineAccessor and ignored on
5823// subsequent calls.
5824static Object* Runtime_DefineAccessor(Arguments args) {
5825 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5826 // Compute attributes.
5827 PropertyAttributes attributes = NONE;
5828 if (args.length() == 5) {
5829 CONVERT_CHECKED(Smi, attrs, args[4]);
5830 int value = attrs->value();
5831 // Only attribute bits should be set.
5832 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5833 attributes = static_cast<PropertyAttributes>(value);
5834 }
5835
5836 CONVERT_CHECKED(JSObject, obj, args[0]);
5837 CONVERT_CHECKED(String, name, args[1]);
5838 CONVERT_CHECKED(Smi, flag, args[2]);
5839 CONVERT_CHECKED(JSFunction, fun, args[3]);
5840 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5841}
5842
5843
5844static Object* Runtime_LookupAccessor(Arguments args) {
5845 ASSERT(args.length() == 3);
5846 CONVERT_CHECKED(JSObject, obj, args[0]);
5847 CONVERT_CHECKED(String, name, args[1]);
5848 CONVERT_CHECKED(Smi, flag, args[2]);
5849 return obj->LookupAccessor(name, flag->value() == 0);
5850}
5851
5852
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005853#ifdef ENABLE_DEBUGGER_SUPPORT
5854static Object* Runtime_DebugBreak(Arguments args) {
5855 ASSERT(args.length() == 0);
5856 return Execution::DebugBreakHelper();
5857}
5858
5859
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005860// Helper functions for wrapping and unwrapping stack frame ids.
5861static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005862 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005863 return Smi::FromInt(id >> 2);
5864}
5865
5866
5867static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5868 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5869}
5870
5871
5872// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005873// args[0]: debug event listener function to set or null or undefined for
5874// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005875// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005876static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005877 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005878 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5879 args[0]->IsUndefined() ||
5880 args[0]->IsNull());
5881 Handle<Object> callback = args.at<Object>(0);
5882 Handle<Object> data = args.at<Object>(1);
5883 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005884
5885 return Heap::undefined_value();
5886}
5887
5888
5889static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005890 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005891 StackGuard::DebugBreak();
5892 return Heap::undefined_value();
5893}
5894
5895
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005896// Find the length of the prototype chain that is to to handled as one. If a
5897// prototype object is hidden it is to be viewed as part of the the object it
5898// is prototype for.
5899static int LocalPrototypeChainLength(JSObject* obj) {
5900 int count = 1;
5901 Object* proto = obj->GetPrototype();
5902 while (proto->IsJSObject() &&
5903 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5904 count++;
5905 proto = JSObject::cast(proto)->GetPrototype();
5906 }
5907 return count;
5908}
5909
5910
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005911static Object* DebugLookupResultValue(Object* receiver, String* name,
5912 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005913 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005914 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005915 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005916 case NORMAL:
5917 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005918 if (value->IsTheHole()) {
5919 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005920 }
5921 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005922 case FIELD:
5923 value =
5924 JSObject::cast(
5925 result->holder())->FastPropertyAt(result->GetFieldIndex());
5926 if (value->IsTheHole()) {
5927 return Heap::undefined_value();
5928 }
5929 return value;
5930 case CONSTANT_FUNCTION:
5931 return result->GetConstantFunction();
5932 case CALLBACKS: {
5933 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005934 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005935 value = receiver->GetPropertyWithCallback(
5936 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005937 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005938 value = Top::pending_exception();
5939 Top::clear_pending_exception();
5940 if (caught_exception != NULL) {
5941 *caught_exception = true;
5942 }
5943 }
5944 return value;
5945 } else {
5946 return Heap::undefined_value();
5947 }
5948 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005949 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005950 case MAP_TRANSITION:
5951 case CONSTANT_TRANSITION:
5952 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005953 return Heap::undefined_value();
5954 default:
5955 UNREACHABLE();
5956 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005957 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005958 return Heap::undefined_value();
5959}
5960
5961
ager@chromium.org32912102009-01-16 10:38:43 +00005962// Get debugger related details for an object property.
5963// args[0]: object holding property
5964// args[1]: name of the property
5965//
5966// The array returned contains the following information:
5967// 0: Property value
5968// 1: Property details
5969// 2: Property value is exception
5970// 3: Getter function if defined
5971// 4: Setter function if defined
5972// Items 2-4 are only filled if the property has either a getter or a setter
5973// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005974static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005975 HandleScope scope;
5976
5977 ASSERT(args.length() == 2);
5978
5979 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5980 CONVERT_ARG_CHECKED(String, name, 1);
5981
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005982 // Make sure to set the current context to the context before the debugger was
5983 // entered (if the debugger is entered). The reason for switching context here
5984 // is that for some property lookups (accessors and interceptors) callbacks
5985 // into the embedding application can occour, and the embedding application
5986 // could have the assumption that its own global context is the current
5987 // context and not some internal debugger context.
5988 SaveContext save;
5989 if (Debug::InDebugger()) {
5990 Top::set_context(*Debug::debugger_entry()->GetContext());
5991 }
5992
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005993 // Skip the global proxy as it has no properties and always delegates to the
5994 // real global object.
5995 if (obj->IsJSGlobalProxy()) {
5996 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5997 }
5998
5999
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006000 // Check if the name is trivially convertible to an index and get the element
6001 // if so.
6002 uint32_t index;
6003 if (name->AsArrayIndex(&index)) {
6004 Handle<FixedArray> details = Factory::NewFixedArray(2);
6005 details->set(0, Runtime::GetElementOrCharAt(obj, index));
6006 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
6007 return *Factory::NewJSArrayWithElements(details);
6008 }
6009
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006010 // Find the number of objects making up this.
6011 int length = LocalPrototypeChainLength(*obj);
6012
6013 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006014 Handle<JSObject> jsproto = obj;
6015 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006016 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006017 jsproto->LocalLookup(*name, &result);
6018 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006019 // LookupResult is not GC safe as it holds raw object pointers.
6020 // GC can happen later in this code so put the required fields into
6021 // local variables using handles when required for later use.
6022 PropertyType result_type = result.type();
6023 Handle<Object> result_callback_obj;
6024 if (result_type == CALLBACKS) {
6025 result_callback_obj = Handle<Object>(result.GetCallbackObject());
6026 }
6027 Smi* property_details = result.GetPropertyDetails().AsSmi();
6028 // DebugLookupResultValue can cause GC so details from LookupResult needs
6029 // to be copied to handles before this.
6030 bool caught_exception = false;
6031 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
6032 &caught_exception);
6033 if (raw_value->IsFailure()) return raw_value;
6034 Handle<Object> value(raw_value);
6035
6036 // If the callback object is a fixed array then it contains JavaScript
6037 // getter and/or setter.
6038 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
6039 result_callback_obj->IsFixedArray();
6040 Handle<FixedArray> details =
6041 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
6042 details->set(0, *value);
6043 details->set(1, property_details);
6044 if (hasJavaScriptAccessors) {
6045 details->set(2,
6046 caught_exception ? Heap::true_value()
6047 : Heap::false_value());
6048 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
6049 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
6050 }
6051
6052 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006053 }
6054 if (i < length - 1) {
6055 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6056 }
6057 }
6058
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006059 return Heap::undefined_value();
6060}
6061
6062
6063static Object* Runtime_DebugGetProperty(Arguments args) {
6064 HandleScope scope;
6065
6066 ASSERT(args.length() == 2);
6067
6068 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6069 CONVERT_ARG_CHECKED(String, name, 1);
6070
6071 LookupResult result;
6072 obj->Lookup(*name, &result);
6073 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006074 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006075 }
6076 return Heap::undefined_value();
6077}
6078
6079
6080// Return the names of the local named properties.
6081// args[0]: object
6082static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
6083 HandleScope scope;
6084 ASSERT(args.length() == 1);
6085 if (!args[0]->IsJSObject()) {
6086 return Heap::undefined_value();
6087 }
6088 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6089
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006090 // Skip the global proxy as it has no properties and always delegates to the
6091 // real global object.
6092 if (obj->IsJSGlobalProxy()) {
6093 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6094 }
6095
6096 // Find the number of objects making up this.
6097 int length = LocalPrototypeChainLength(*obj);
6098
6099 // Find the number of local properties for each of the objects.
6100 int* local_property_count = NewArray<int>(length);
6101 int total_property_count = 0;
6102 Handle<JSObject> jsproto = obj;
6103 for (int i = 0; i < length; i++) {
6104 int n;
6105 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
6106 local_property_count[i] = n;
6107 total_property_count += n;
6108 if (i < length - 1) {
6109 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6110 }
6111 }
6112
6113 // Allocate an array with storage for all the property names.
6114 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
6115
6116 // Get the property names.
6117 jsproto = obj;
ager@chromium.orgc730f772009-11-11 10:11:16 +00006118 int proto_with_hidden_properties = 0;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006119 for (int i = 0; i < length; i++) {
6120 jsproto->GetLocalPropertyNames(*names,
6121 i == 0 ? 0 : local_property_count[i - 1]);
ager@chromium.orgc730f772009-11-11 10:11:16 +00006122 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
6123 proto_with_hidden_properties++;
6124 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006125 if (i < length - 1) {
6126 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6127 }
6128 }
6129
ager@chromium.orgc730f772009-11-11 10:11:16 +00006130 // Filter out name of hidden propeties object.
6131 if (proto_with_hidden_properties > 0) {
6132 Handle<FixedArray> old_names = names;
6133 names = Factory::NewFixedArray(
6134 names->length() - proto_with_hidden_properties);
6135 int dest_pos = 0;
6136 for (int i = 0; i < total_property_count; i++) {
6137 Object* name = old_names->get(i);
6138 if (name == Heap::hidden_symbol()) {
6139 continue;
6140 }
6141 names->set(dest_pos++, name);
6142 }
6143 }
6144
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006145 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006146 return *Factory::NewJSArrayWithElements(names);
6147}
6148
6149
6150// Return the names of the local indexed properties.
6151// args[0]: object
6152static Object* Runtime_DebugLocalElementNames(Arguments args) {
6153 HandleScope scope;
6154 ASSERT(args.length() == 1);
6155 if (!args[0]->IsJSObject()) {
6156 return Heap::undefined_value();
6157 }
6158 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6159
6160 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
6161 Handle<FixedArray> names = Factory::NewFixedArray(n);
6162 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
6163 return *Factory::NewJSArrayWithElements(names);
6164}
6165
6166
6167// Return the property type calculated from the property details.
6168// args[0]: smi with property details.
6169static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6170 ASSERT(args.length() == 1);
6171 CONVERT_CHECKED(Smi, details, args[0]);
6172 PropertyType type = PropertyDetails(details).type();
6173 return Smi::FromInt(static_cast<int>(type));
6174}
6175
6176
6177// Return the property attribute calculated from the property details.
6178// args[0]: smi with property details.
6179static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6180 ASSERT(args.length() == 1);
6181 CONVERT_CHECKED(Smi, details, args[0]);
6182 PropertyAttributes attributes = PropertyDetails(details).attributes();
6183 return Smi::FromInt(static_cast<int>(attributes));
6184}
6185
6186
6187// Return the property insertion index calculated from the property details.
6188// args[0]: smi with property details.
6189static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6190 ASSERT(args.length() == 1);
6191 CONVERT_CHECKED(Smi, details, args[0]);
6192 int index = PropertyDetails(details).index();
6193 return Smi::FromInt(index);
6194}
6195
6196
6197// Return information on whether an object has a named or indexed interceptor.
6198// args[0]: object
6199static Object* Runtime_DebugInterceptorInfo(Arguments args) {
6200 HandleScope scope;
6201 ASSERT(args.length() == 1);
6202 if (!args[0]->IsJSObject()) {
6203 return Smi::FromInt(0);
6204 }
6205 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6206
6207 int result = 0;
6208 if (obj->HasNamedInterceptor()) result |= 2;
6209 if (obj->HasIndexedInterceptor()) result |= 1;
6210
6211 return Smi::FromInt(result);
6212}
6213
6214
6215// Return property names from named interceptor.
6216// args[0]: object
6217static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
6218 HandleScope scope;
6219 ASSERT(args.length() == 1);
6220 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006221
ager@chromium.org32912102009-01-16 10:38:43 +00006222 if (obj->HasNamedInterceptor()) {
6223 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
6224 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6225 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006226 return Heap::undefined_value();
6227}
6228
6229
6230// Return element names from indexed interceptor.
6231// args[0]: object
6232static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
6233 HandleScope scope;
6234 ASSERT(args.length() == 1);
6235 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006236
ager@chromium.org32912102009-01-16 10:38:43 +00006237 if (obj->HasIndexedInterceptor()) {
6238 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
6239 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6240 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006241 return Heap::undefined_value();
6242}
6243
6244
6245// Return property value from named interceptor.
6246// args[0]: object
6247// args[1]: property name
6248static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6249 HandleScope scope;
6250 ASSERT(args.length() == 2);
6251 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6252 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6253 CONVERT_ARG_CHECKED(String, name, 1);
6254
6255 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006256 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006257}
6258
6259
6260// Return element value from indexed interceptor.
6261// args[0]: object
6262// args[1]: index
6263static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6264 HandleScope scope;
6265 ASSERT(args.length() == 2);
6266 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6267 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6268 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6269
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006270 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006271}
6272
6273
6274static Object* Runtime_CheckExecutionState(Arguments args) {
6275 ASSERT(args.length() >= 1);
6276 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006277 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006278 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006279 return Top::Throw(Heap::illegal_execution_state_symbol());
6280 }
6281
6282 return Heap::true_value();
6283}
6284
6285
6286static Object* Runtime_GetFrameCount(Arguments args) {
6287 HandleScope scope;
6288 ASSERT(args.length() == 1);
6289
6290 // Check arguments.
6291 Object* result = Runtime_CheckExecutionState(args);
6292 if (result->IsFailure()) return result;
6293
6294 // Count all frames which are relevant to debugging stack trace.
6295 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006296 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006297 if (id == StackFrame::NO_ID) {
6298 // If there is no JavaScript stack frame count is 0.
6299 return Smi::FromInt(0);
6300 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006301 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6302 return Smi::FromInt(n);
6303}
6304
6305
6306static const int kFrameDetailsFrameIdIndex = 0;
6307static const int kFrameDetailsReceiverIndex = 1;
6308static const int kFrameDetailsFunctionIndex = 2;
6309static const int kFrameDetailsArgumentCountIndex = 3;
6310static const int kFrameDetailsLocalCountIndex = 4;
6311static const int kFrameDetailsSourcePositionIndex = 5;
6312static const int kFrameDetailsConstructCallIndex = 6;
6313static const int kFrameDetailsDebuggerFrameIndex = 7;
6314static const int kFrameDetailsFirstDynamicIndex = 8;
6315
6316// Return an array with frame details
6317// args[0]: number: break id
6318// args[1]: number: frame index
6319//
6320// The array returned contains the following information:
6321// 0: Frame id
6322// 1: Receiver
6323// 2: Function
6324// 3: Argument count
6325// 4: Local count
6326// 5: Source position
6327// 6: Constructor call
6328// 7: Debugger frame
6329// Arguments name, value
6330// Locals name, value
6331static Object* Runtime_GetFrameDetails(Arguments args) {
6332 HandleScope scope;
6333 ASSERT(args.length() == 2);
6334
6335 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006336 Object* check = Runtime_CheckExecutionState(args);
6337 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006338 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6339
6340 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006341 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006342 if (id == StackFrame::NO_ID) {
6343 // If there are no JavaScript stack frames return undefined.
6344 return Heap::undefined_value();
6345 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006346 int count = 0;
6347 JavaScriptFrameIterator it(id);
6348 for (; !it.done(); it.Advance()) {
6349 if (count == index) break;
6350 count++;
6351 }
6352 if (it.done()) return Heap::undefined_value();
6353
6354 // Traverse the saved contexts chain to find the active context for the
6355 // selected frame.
6356 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006357 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006358 save = save->prev();
6359 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006360 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006361
6362 // Get the frame id.
6363 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6364
6365 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006366 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006367
6368 // Check for constructor frame.
6369 bool constructor = it.frame()->IsConstructor();
6370
6371 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006372 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006373 ScopeInfo<> info(*code);
6374
6375 // Get the context.
6376 Handle<Context> context(Context::cast(it.frame()->context()));
6377
6378 // Get the locals names and values into a temporary array.
6379 //
6380 // TODO(1240907): Hide compiler-introduced stack variables
6381 // (e.g. .result)? For users of the debugger, they will probably be
6382 // confusing.
6383 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6384 for (int i = 0; i < info.NumberOfLocals(); i++) {
6385 // Name of the local.
6386 locals->set(i * 2, *info.LocalName(i));
6387
6388 // Fetch the value of the local - either from the stack or from a
6389 // heap-allocated context.
6390 if (i < info.number_of_stack_slots()) {
6391 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6392 } else {
6393 Handle<String> name = info.LocalName(i);
6394 // Traverse the context chain to the function context as all local
6395 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006396 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006397 context = Handle<Context>(context->previous());
6398 }
6399 ASSERT(context->is_function_context());
6400 locals->set(i * 2 + 1,
6401 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6402 NULL)));
6403 }
6404 }
6405
6406 // Now advance to the arguments adapter frame (if any). If contains all
6407 // the provided parameters and
6408
6409 // Now advance to the arguments adapter frame (if any). It contains all
6410 // the provided parameters whereas the function frame always have the number
6411 // of arguments matching the functions parameters. The rest of the
6412 // information (except for what is collected above) is the same.
6413 it.AdvanceToArgumentsFrame();
6414
6415 // Find the number of arguments to fill. At least fill the number of
6416 // parameters for the function and fill more if more parameters are provided.
6417 int argument_count = info.number_of_parameters();
6418 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6419 argument_count = it.frame()->GetProvidedParametersCount();
6420 }
6421
6422 // Calculate the size of the result.
6423 int details_size = kFrameDetailsFirstDynamicIndex +
6424 2 * (argument_count + info.NumberOfLocals());
6425 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6426
6427 // Add the frame id.
6428 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6429
6430 // Add the function (same as in function frame).
6431 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6432
6433 // Add the arguments count.
6434 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6435
6436 // Add the locals count
6437 details->set(kFrameDetailsLocalCountIndex,
6438 Smi::FromInt(info.NumberOfLocals()));
6439
6440 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006441 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006442 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6443 } else {
6444 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6445 }
6446
6447 // Add the constructor information.
6448 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6449
6450 // Add information on whether this frame is invoked in the debugger context.
6451 details->set(kFrameDetailsDebuggerFrameIndex,
6452 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6453
6454 // Fill the dynamic part.
6455 int details_index = kFrameDetailsFirstDynamicIndex;
6456
6457 // Add arguments name and value.
6458 for (int i = 0; i < argument_count; i++) {
6459 // Name of the argument.
6460 if (i < info.number_of_parameters()) {
6461 details->set(details_index++, *info.parameter_name(i));
6462 } else {
6463 details->set(details_index++, Heap::undefined_value());
6464 }
6465
6466 // Parameter value.
6467 if (i < it.frame()->GetProvidedParametersCount()) {
6468 details->set(details_index++, it.frame()->GetParameter(i));
6469 } else {
6470 details->set(details_index++, Heap::undefined_value());
6471 }
6472 }
6473
6474 // Add locals name and value from the temporary copy from the function frame.
6475 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6476 details->set(details_index++, locals->get(i));
6477 }
6478
6479 // Add the receiver (same as in function frame).
6480 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6481 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6482 Handle<Object> receiver(it.frame()->receiver());
6483 if (!receiver->IsJSObject()) {
6484 // If the receiver is NOT a JSObject we have hit an optimization
6485 // where a value object is not converted into a wrapped JS objects.
6486 // To hide this optimization from the debugger, we wrap the receiver
6487 // by creating correct wrapper object based on the calling frame's
6488 // global context.
6489 it.Advance();
6490 Handle<Context> calling_frames_global_context(
6491 Context::cast(Context::cast(it.frame()->context())->global_context()));
6492 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6493 }
6494 details->set(kFrameDetailsReceiverIndex, *receiver);
6495
6496 ASSERT_EQ(details_size, details_index);
6497 return *Factory::NewJSArrayWithElements(details);
6498}
6499
6500
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006501// Copy all the context locals into an object used to materialize a scope.
6502static void CopyContextLocalsToScopeObject(Handle<Code> code,
6503 ScopeInfo<>& scope_info,
6504 Handle<Context> context,
6505 Handle<JSObject> scope_object) {
6506 // Fill all context locals to the context extension.
6507 for (int i = Context::MIN_CONTEXT_SLOTS;
6508 i < scope_info.number_of_context_slots();
6509 i++) {
6510 int context_index =
6511 ScopeInfo<>::ContextSlotIndex(*code,
6512 *scope_info.context_slot_name(i),
6513 NULL);
6514
6515 // Don't include the arguments shadow (.arguments) context variable.
6516 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6517 SetProperty(scope_object,
6518 scope_info.context_slot_name(i),
6519 Handle<Object>(context->get(context_index)), NONE);
6520 }
6521 }
6522}
6523
6524
6525// Create a plain JSObject which materializes the local scope for the specified
6526// frame.
6527static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6528 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6529 Handle<Code> code(function->code());
6530 ScopeInfo<> scope_info(*code);
6531
6532 // Allocate and initialize a JSObject with all the arguments, stack locals
6533 // heap locals and extension properties of the debugged function.
6534 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6535
6536 // First fill all parameters.
6537 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6538 SetProperty(local_scope,
6539 scope_info.parameter_name(i),
6540 Handle<Object>(frame->GetParameter(i)), NONE);
6541 }
6542
6543 // Second fill all stack locals.
6544 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6545 SetProperty(local_scope,
6546 scope_info.stack_slot_name(i),
6547 Handle<Object>(frame->GetExpression(i)), NONE);
6548 }
6549
6550 // Third fill all context locals.
6551 Handle<Context> frame_context(Context::cast(frame->context()));
6552 Handle<Context> function_context(frame_context->fcontext());
6553 CopyContextLocalsToScopeObject(code, scope_info,
6554 function_context, local_scope);
6555
6556 // Finally copy any properties from the function context extension. This will
6557 // be variables introduced by eval.
6558 if (function_context->closure() == *function) {
6559 if (function_context->has_extension() &&
6560 !function_context->IsGlobalContext()) {
6561 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006562 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006563 for (int i = 0; i < keys->length(); i++) {
6564 // Names of variables introduced by eval are strings.
6565 ASSERT(keys->get(i)->IsString());
6566 Handle<String> key(String::cast(keys->get(i)));
6567 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6568 }
6569 }
6570 }
6571 return local_scope;
6572}
6573
6574
6575// Create a plain JSObject which materializes the closure content for the
6576// context.
6577static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6578 ASSERT(context->is_function_context());
6579
6580 Handle<Code> code(context->closure()->code());
6581 ScopeInfo<> scope_info(*code);
6582
6583 // Allocate and initialize a JSObject with all the content of theis function
6584 // closure.
6585 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6586
6587 // Check whether the arguments shadow object exists.
6588 int arguments_shadow_index =
6589 ScopeInfo<>::ContextSlotIndex(*code,
6590 Heap::arguments_shadow_symbol(),
6591 NULL);
6592 if (arguments_shadow_index >= 0) {
6593 // In this case all the arguments are available in the arguments shadow
6594 // object.
6595 Handle<JSObject> arguments_shadow(
6596 JSObject::cast(context->get(arguments_shadow_index)));
6597 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6598 SetProperty(closure_scope,
6599 scope_info.parameter_name(i),
6600 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6601 }
6602 }
6603
6604 // Fill all context locals to the context extension.
6605 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6606
6607 // Finally copy any properties from the function context extension. This will
6608 // be variables introduced by eval.
6609 if (context->has_extension()) {
6610 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006611 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006612 for (int i = 0; i < keys->length(); i++) {
6613 // Names of variables introduced by eval are strings.
6614 ASSERT(keys->get(i)->IsString());
6615 Handle<String> key(String::cast(keys->get(i)));
6616 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6617 }
6618 }
6619
6620 return closure_scope;
6621}
6622
6623
6624// Iterate over the actual scopes visible from a stack frame. All scopes are
6625// backed by an actual context except the local scope, which is inserted
6626// "artifically" in the context chain.
6627class ScopeIterator {
6628 public:
6629 enum ScopeType {
6630 ScopeTypeGlobal = 0,
6631 ScopeTypeLocal,
6632 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006633 ScopeTypeClosure,
6634 // Every catch block contains an implicit with block (its parameter is
6635 // a JSContextExtensionObject) that extends current scope with a variable
6636 // holding exception object. Such with blocks are treated as scopes of their
6637 // own type.
6638 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006639 };
6640
6641 explicit ScopeIterator(JavaScriptFrame* frame)
6642 : frame_(frame),
6643 function_(JSFunction::cast(frame->function())),
6644 context_(Context::cast(frame->context())),
6645 local_done_(false),
6646 at_local_(false) {
6647
6648 // Check whether the first scope is actually a local scope.
6649 if (context_->IsGlobalContext()) {
6650 // If there is a stack slot for .result then this local scope has been
6651 // created for evaluating top level code and it is not a real local scope.
6652 // Checking for the existence of .result seems fragile, but the scope info
6653 // saved with the code object does not otherwise have that information.
6654 Handle<Code> code(function_->code());
6655 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6656 at_local_ = index < 0;
6657 } else if (context_->is_function_context()) {
6658 at_local_ = true;
6659 }
6660 }
6661
6662 // More scopes?
6663 bool Done() { return context_.is_null(); }
6664
6665 // Move to the next scope.
6666 void Next() {
6667 // If at a local scope mark the local scope as passed.
6668 if (at_local_) {
6669 at_local_ = false;
6670 local_done_ = true;
6671
6672 // If the current context is not associated with the local scope the
6673 // current context is the next real scope, so don't move to the next
6674 // context in this case.
6675 if (context_->closure() != *function_) {
6676 return;
6677 }
6678 }
6679
6680 // The global scope is always the last in the chain.
6681 if (context_->IsGlobalContext()) {
6682 context_ = Handle<Context>();
6683 return;
6684 }
6685
6686 // Move to the next context.
6687 if (context_->is_function_context()) {
6688 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6689 } else {
6690 context_ = Handle<Context>(context_->previous());
6691 }
6692
6693 // If passing the local scope indicate that the current scope is now the
6694 // local scope.
6695 if (!local_done_ &&
6696 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6697 at_local_ = true;
6698 }
6699 }
6700
6701 // Return the type of the current scope.
6702 int Type() {
6703 if (at_local_) {
6704 return ScopeTypeLocal;
6705 }
6706 if (context_->IsGlobalContext()) {
6707 ASSERT(context_->global()->IsGlobalObject());
6708 return ScopeTypeGlobal;
6709 }
6710 if (context_->is_function_context()) {
6711 return ScopeTypeClosure;
6712 }
6713 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006714 // Current scope is either an explicit with statement or a with statement
6715 // implicitely generated for a catch block.
6716 // If the extension object here is a JSContextExtensionObject then
6717 // current with statement is one frome a catch block otherwise it's a
6718 // regular with statement.
6719 if (context_->extension()->IsJSContextExtensionObject()) {
6720 return ScopeTypeCatch;
6721 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006722 return ScopeTypeWith;
6723 }
6724
6725 // Return the JavaScript object with the content of the current scope.
6726 Handle<JSObject> ScopeObject() {
6727 switch (Type()) {
6728 case ScopeIterator::ScopeTypeGlobal:
6729 return Handle<JSObject>(CurrentContext()->global());
6730 break;
6731 case ScopeIterator::ScopeTypeLocal:
6732 // Materialize the content of the local scope into a JSObject.
6733 return MaterializeLocalScope(frame_);
6734 break;
6735 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006736 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006737 // Return the with object.
6738 return Handle<JSObject>(CurrentContext()->extension());
6739 break;
6740 case ScopeIterator::ScopeTypeClosure:
6741 // Materialize the content of the closure scope into a JSObject.
6742 return MaterializeClosure(CurrentContext());
6743 break;
6744 }
6745 UNREACHABLE();
6746 return Handle<JSObject>();
6747 }
6748
6749 // Return the context for this scope. For the local context there might not
6750 // be an actual context.
6751 Handle<Context> CurrentContext() {
6752 if (at_local_ && context_->closure() != *function_) {
6753 return Handle<Context>();
6754 }
6755 return context_;
6756 }
6757
6758#ifdef DEBUG
6759 // Debug print of the content of the current scope.
6760 void DebugPrint() {
6761 switch (Type()) {
6762 case ScopeIterator::ScopeTypeGlobal:
6763 PrintF("Global:\n");
6764 CurrentContext()->Print();
6765 break;
6766
6767 case ScopeIterator::ScopeTypeLocal: {
6768 PrintF("Local:\n");
6769 Handle<Code> code(function_->code());
6770 ScopeInfo<> scope_info(*code);
6771 scope_info.Print();
6772 if (!CurrentContext().is_null()) {
6773 CurrentContext()->Print();
6774 if (CurrentContext()->has_extension()) {
6775 Handle<JSObject> extension =
6776 Handle<JSObject>(CurrentContext()->extension());
6777 if (extension->IsJSContextExtensionObject()) {
6778 extension->Print();
6779 }
6780 }
6781 }
6782 break;
6783 }
6784
6785 case ScopeIterator::ScopeTypeWith: {
6786 PrintF("With:\n");
6787 Handle<JSObject> extension =
6788 Handle<JSObject>(CurrentContext()->extension());
6789 extension->Print();
6790 break;
6791 }
6792
ager@chromium.orga1645e22009-09-09 19:27:10 +00006793 case ScopeIterator::ScopeTypeCatch: {
6794 PrintF("Catch:\n");
6795 Handle<JSObject> extension =
6796 Handle<JSObject>(CurrentContext()->extension());
6797 extension->Print();
6798 break;
6799 }
6800
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006801 case ScopeIterator::ScopeTypeClosure: {
6802 PrintF("Closure:\n");
6803 CurrentContext()->Print();
6804 if (CurrentContext()->has_extension()) {
6805 Handle<JSObject> extension =
6806 Handle<JSObject>(CurrentContext()->extension());
6807 if (extension->IsJSContextExtensionObject()) {
6808 extension->Print();
6809 }
6810 }
6811 break;
6812 }
6813
6814 default:
6815 UNREACHABLE();
6816 }
6817 PrintF("\n");
6818 }
6819#endif
6820
6821 private:
6822 JavaScriptFrame* frame_;
6823 Handle<JSFunction> function_;
6824 Handle<Context> context_;
6825 bool local_done_;
6826 bool at_local_;
6827
6828 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6829};
6830
6831
6832static Object* Runtime_GetScopeCount(Arguments args) {
6833 HandleScope scope;
6834 ASSERT(args.length() == 2);
6835
6836 // Check arguments.
6837 Object* check = Runtime_CheckExecutionState(args);
6838 if (check->IsFailure()) return check;
6839 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6840
6841 // Get the frame where the debugging is performed.
6842 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6843 JavaScriptFrameIterator it(id);
6844 JavaScriptFrame* frame = it.frame();
6845
6846 // Count the visible scopes.
6847 int n = 0;
6848 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6849 n++;
6850 }
6851
6852 return Smi::FromInt(n);
6853}
6854
6855
6856static const int kScopeDetailsTypeIndex = 0;
6857static const int kScopeDetailsObjectIndex = 1;
6858static const int kScopeDetailsSize = 2;
6859
6860// Return an array with scope details
6861// args[0]: number: break id
6862// args[1]: number: frame index
6863// args[2]: number: scope index
6864//
6865// The array returned contains the following information:
6866// 0: Scope type
6867// 1: Scope object
6868static Object* Runtime_GetScopeDetails(Arguments args) {
6869 HandleScope scope;
6870 ASSERT(args.length() == 3);
6871
6872 // Check arguments.
6873 Object* check = Runtime_CheckExecutionState(args);
6874 if (check->IsFailure()) return check;
6875 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6876 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6877
6878 // Get the frame where the debugging is performed.
6879 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6880 JavaScriptFrameIterator frame_it(id);
6881 JavaScriptFrame* frame = frame_it.frame();
6882
6883 // Find the requested scope.
6884 int n = 0;
6885 ScopeIterator it(frame);
6886 for (; !it.Done() && n < index; it.Next()) {
6887 n++;
6888 }
6889 if (it.Done()) {
6890 return Heap::undefined_value();
6891 }
6892
6893 // Calculate the size of the result.
6894 int details_size = kScopeDetailsSize;
6895 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6896
6897 // Fill in scope details.
6898 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6899 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6900
6901 return *Factory::NewJSArrayWithElements(details);
6902}
6903
6904
6905static Object* Runtime_DebugPrintScopes(Arguments args) {
6906 HandleScope scope;
6907 ASSERT(args.length() == 0);
6908
6909#ifdef DEBUG
6910 // Print the scopes for the top frame.
6911 StackFrameLocator locator;
6912 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6913 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6914 it.DebugPrint();
6915 }
6916#endif
6917 return Heap::undefined_value();
6918}
6919
6920
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006921static Object* Runtime_GetCFrames(Arguments args) {
6922 HandleScope scope;
6923 ASSERT(args.length() == 1);
6924 Object* result = Runtime_CheckExecutionState(args);
6925 if (result->IsFailure()) return result;
6926
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006927#if V8_HOST_ARCH_64_BIT
6928 UNIMPLEMENTED();
6929 return Heap::undefined_value();
6930#else
6931
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006933 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6934 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006935 if (frames_count == OS::kStackWalkError) {
6936 return Heap::undefined_value();
6937 }
6938
6939 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6940 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6941 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6942 for (int i = 0; i < frames_count; i++) {
6943 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6944 frame_value->SetProperty(
6945 *address_str,
6946 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6947 NONE);
6948
6949 // Get the stack walk text for this frame.
6950 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006951 int frame_text_length = StrLength(frames[i].text);
6952 if (frame_text_length > 0) {
6953 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006954 frame_text = Factory::NewStringFromAscii(str);
6955 }
6956
6957 if (!frame_text.is_null()) {
6958 frame_value->SetProperty(*text_str, *frame_text, NONE);
6959 }
6960
6961 frames_array->set(i, *frame_value);
6962 }
6963 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006964#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006965}
6966
6967
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006968static Object* Runtime_GetThreadCount(Arguments args) {
6969 HandleScope scope;
6970 ASSERT(args.length() == 1);
6971
6972 // Check arguments.
6973 Object* result = Runtime_CheckExecutionState(args);
6974 if (result->IsFailure()) return result;
6975
6976 // Count all archived V8 threads.
6977 int n = 0;
6978 for (ThreadState* thread = ThreadState::FirstInUse();
6979 thread != NULL;
6980 thread = thread->Next()) {
6981 n++;
6982 }
6983
6984 // Total number of threads is current thread and archived threads.
6985 return Smi::FromInt(n + 1);
6986}
6987
6988
6989static const int kThreadDetailsCurrentThreadIndex = 0;
6990static const int kThreadDetailsThreadIdIndex = 1;
6991static const int kThreadDetailsSize = 2;
6992
6993// Return an array with thread details
6994// args[0]: number: break id
6995// args[1]: number: thread index
6996//
6997// The array returned contains the following information:
6998// 0: Is current thread?
6999// 1: Thread id
7000static Object* Runtime_GetThreadDetails(Arguments args) {
7001 HandleScope scope;
7002 ASSERT(args.length() == 2);
7003
7004 // Check arguments.
7005 Object* check = Runtime_CheckExecutionState(args);
7006 if (check->IsFailure()) return check;
7007 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
7008
7009 // Allocate array for result.
7010 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
7011
7012 // Thread index 0 is current thread.
7013 if (index == 0) {
7014 // Fill the details.
7015 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
7016 details->set(kThreadDetailsThreadIdIndex,
7017 Smi::FromInt(ThreadManager::CurrentId()));
7018 } else {
7019 // Find the thread with the requested index.
7020 int n = 1;
7021 ThreadState* thread = ThreadState::FirstInUse();
7022 while (index != n && thread != NULL) {
7023 thread = thread->Next();
7024 n++;
7025 }
7026 if (thread == NULL) {
7027 return Heap::undefined_value();
7028 }
7029
7030 // Fill the details.
7031 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
7032 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
7033 }
7034
7035 // Convert to JS array and return.
7036 return *Factory::NewJSArrayWithElements(details);
7037}
7038
7039
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007040static Object* Runtime_GetBreakLocations(Arguments args) {
7041 HandleScope scope;
7042 ASSERT(args.length() == 1);
7043
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007044 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7045 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007046 // Find the number of break points
7047 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
7048 if (break_locations->IsUndefined()) return Heap::undefined_value();
7049 // Return array as JS array
7050 return *Factory::NewJSArrayWithElements(
7051 Handle<FixedArray>::cast(break_locations));
7052}
7053
7054
7055// Set a break point in a function
7056// args[0]: function
7057// args[1]: number: break source position (within the function source)
7058// args[2]: number: break point object
7059static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
7060 HandleScope scope;
7061 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007062 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7063 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007064 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7065 RUNTIME_ASSERT(source_position >= 0);
7066 Handle<Object> break_point_object_arg = args.at<Object>(2);
7067
7068 // Set break point.
7069 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
7070
7071 return Heap::undefined_value();
7072}
7073
7074
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007075Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
7076 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007077 // Iterate the heap looking for SharedFunctionInfo generated from the
7078 // script. The inner most SharedFunctionInfo containing the source position
7079 // for the requested break point is found.
7080 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
7081 // which is found is not compiled it is compiled and the heap is iterated
7082 // again as the compilation might create inner functions from the newly
7083 // compiled function and the actual requested break point might be in one of
7084 // these functions.
7085 bool done = false;
7086 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00007087 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007088 Handle<SharedFunctionInfo> target;
7089 // The current candidate for the last function in script:
7090 Handle<SharedFunctionInfo> last;
7091 while (!done) {
7092 HeapIterator iterator;
7093 while (iterator.has_next()) {
7094 HeapObject* obj = iterator.next();
7095 ASSERT(obj != NULL);
7096 if (obj->IsSharedFunctionInfo()) {
7097 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
7098 if (shared->script() == *script) {
7099 // If the SharedFunctionInfo found has the requested script data and
7100 // contains the source position it is a candidate.
7101 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00007102 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007103 start_position = shared->start_position();
7104 }
7105 if (start_position <= position &&
7106 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007107 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108 // candidate this is the new candidate.
7109 if (target.is_null()) {
7110 target_start_position = start_position;
7111 target = shared;
7112 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007113 if (target_start_position == start_position &&
7114 shared->end_position() == target->end_position()) {
7115 // If a top-level function contain only one function
7116 // declartion the source for the top-level and the function is
7117 // the same. In that case prefer the non top-level function.
7118 if (!shared->is_toplevel()) {
7119 target_start_position = start_position;
7120 target = shared;
7121 }
7122 } else if (target_start_position <= start_position &&
7123 shared->end_position() <= target->end_position()) {
7124 // This containment check includes equality as a function inside
7125 // a top-level function can share either start or end position
7126 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127 target_start_position = start_position;
7128 target = shared;
7129 }
7130 }
7131 }
7132
7133 // Keep track of the last function in the script.
7134 if (last.is_null() ||
7135 shared->end_position() > last->start_position()) {
7136 last = shared;
7137 }
7138 }
7139 }
7140 }
7141
7142 // Make sure some candidate is selected.
7143 if (target.is_null()) {
7144 if (!last.is_null()) {
7145 // Position after the last function - use last.
7146 target = last;
7147 } else {
7148 // Unable to find function - possibly script without any function.
7149 return Heap::undefined_value();
7150 }
7151 }
7152
7153 // If the candidate found is compiled we are done. NOTE: when lazy
7154 // compilation of inner functions is introduced some additional checking
7155 // needs to be done here to compile inner functions.
7156 done = target->is_compiled();
7157 if (!done) {
7158 // If the candidate is not compiled compile it to reveal any inner
7159 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007160 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007161 }
7162 }
7163
7164 return *target;
7165}
7166
7167
7168// Change the state of a break point in a script. NOTE: Regarding performance
7169// see the NOTE for GetScriptFromScriptData.
7170// args[0]: script to set break point in
7171// args[1]: number: break source position (within the script source)
7172// args[2]: number: break point object
7173static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7174 HandleScope scope;
7175 ASSERT(args.length() == 3);
7176 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7177 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7178 RUNTIME_ASSERT(source_position >= 0);
7179 Handle<Object> break_point_object_arg = args.at<Object>(2);
7180
7181 // Get the script from the script wrapper.
7182 RUNTIME_ASSERT(wrapper->value()->IsScript());
7183 Handle<Script> script(Script::cast(wrapper->value()));
7184
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007185 Object* result = Runtime::FindSharedFunctionInfoInScript(
7186 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007187 if (!result->IsUndefined()) {
7188 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7189 // Find position within function. The script position might be before the
7190 // source position of the first function.
7191 int position;
7192 if (shared->start_position() > source_position) {
7193 position = 0;
7194 } else {
7195 position = source_position - shared->start_position();
7196 }
7197 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7198 }
7199 return Heap::undefined_value();
7200}
7201
7202
7203// Clear a break point
7204// args[0]: number: break point object
7205static Object* Runtime_ClearBreakPoint(Arguments args) {
7206 HandleScope scope;
7207 ASSERT(args.length() == 1);
7208 Handle<Object> break_point_object_arg = args.at<Object>(0);
7209
7210 // Clear break point.
7211 Debug::ClearBreakPoint(break_point_object_arg);
7212
7213 return Heap::undefined_value();
7214}
7215
7216
7217// Change the state of break on exceptions
7218// args[0]: boolean indicating uncaught exceptions
7219// args[1]: boolean indicating on/off
7220static Object* Runtime_ChangeBreakOnException(Arguments args) {
7221 HandleScope scope;
7222 ASSERT(args.length() == 2);
7223 ASSERT(args[0]->IsNumber());
7224 ASSERT(args[1]->IsBoolean());
7225
7226 // Update break point state
7227 ExceptionBreakType type =
7228 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7229 bool enable = args[1]->ToBoolean()->IsTrue();
7230 Debug::ChangeBreakOnException(type, enable);
7231 return Heap::undefined_value();
7232}
7233
7234
7235// Prepare for stepping
7236// args[0]: break id for checking execution state
7237// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007238// args[2]: number of times to perform the step, for step out it is the number
7239// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007240static Object* Runtime_PrepareStep(Arguments args) {
7241 HandleScope scope;
7242 ASSERT(args.length() == 3);
7243 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007244 Object* check = Runtime_CheckExecutionState(args);
7245 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007246 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7247 return Top::Throw(Heap::illegal_argument_symbol());
7248 }
7249
7250 // Get the step action and check validity.
7251 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7252 if (step_action != StepIn &&
7253 step_action != StepNext &&
7254 step_action != StepOut &&
7255 step_action != StepInMin &&
7256 step_action != StepMin) {
7257 return Top::Throw(Heap::illegal_argument_symbol());
7258 }
7259
7260 // Get the number of steps.
7261 int step_count = NumberToInt32(args[2]);
7262 if (step_count < 1) {
7263 return Top::Throw(Heap::illegal_argument_symbol());
7264 }
7265
ager@chromium.orga1645e22009-09-09 19:27:10 +00007266 // Clear all current stepping setup.
7267 Debug::ClearStepping();
7268
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007269 // Prepare step.
7270 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7271 return Heap::undefined_value();
7272}
7273
7274
7275// Clear all stepping set by PrepareStep.
7276static Object* Runtime_ClearStepping(Arguments args) {
7277 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007278 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007279 Debug::ClearStepping();
7280 return Heap::undefined_value();
7281}
7282
7283
7284// Creates a copy of the with context chain. The copy of the context chain is
7285// is linked to the function context supplied.
7286static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7287 Handle<Context> function_context) {
7288 // At the bottom of the chain. Return the function context to link to.
7289 if (context_chain->is_function_context()) {
7290 return function_context;
7291 }
7292
7293 // Recursively copy the with contexts.
7294 Handle<Context> previous(context_chain->previous());
7295 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7296 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007297 CopyWithContextChain(function_context, previous),
7298 extension,
7299 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007300}
7301
7302
7303// Helper function to find or create the arguments object for
7304// Runtime_DebugEvaluate.
7305static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7306 Handle<JSFunction> function,
7307 Handle<Code> code,
7308 const ScopeInfo<>* sinfo,
7309 Handle<Context> function_context) {
7310 // Try to find the value of 'arguments' to pass as parameter. If it is not
7311 // found (that is the debugged function does not reference 'arguments' and
7312 // does not support eval) then create an 'arguments' object.
7313 int index;
7314 if (sinfo->number_of_stack_slots() > 0) {
7315 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7316 if (index != -1) {
7317 return Handle<Object>(frame->GetExpression(index));
7318 }
7319 }
7320
7321 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7322 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7323 NULL);
7324 if (index != -1) {
7325 return Handle<Object>(function_context->get(index));
7326 }
7327 }
7328
7329 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007330 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7331 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007332 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007333 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007334 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007335 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007336 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007337 return arguments;
7338}
7339
7340
7341// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007342// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007343// extension part has all the parameters and locals of the function on the
7344// stack frame. A function which calls eval with the code to evaluate is then
7345// compiled in this context and called in this context. As this context
7346// replaces the context of the function on the stack frame a new (empty)
7347// function is created as well to be used as the closure for the context.
7348// This function and the context acts as replacements for the function on the
7349// stack frame presenting the same view of the values of parameters and
7350// local variables as if the piece of JavaScript was evaluated at the point
7351// where the function on the stack frame is currently stopped.
7352static Object* Runtime_DebugEvaluate(Arguments args) {
7353 HandleScope scope;
7354
7355 // Check the execution state and decode arguments frame and source to be
7356 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007357 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007358 Object* check_result = Runtime_CheckExecutionState(args);
7359 if (check_result->IsFailure()) return check_result;
7360 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7361 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007362 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7363
7364 // Handle the processing of break.
7365 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007366
7367 // Get the frame where the debugging is performed.
7368 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7369 JavaScriptFrameIterator it(id);
7370 JavaScriptFrame* frame = it.frame();
7371 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7372 Handle<Code> code(function->code());
7373 ScopeInfo<> sinfo(*code);
7374
7375 // Traverse the saved contexts chain to find the active context for the
7376 // selected frame.
7377 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007378 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007379 save = save->prev();
7380 }
7381 ASSERT(save != NULL);
7382 SaveContext savex;
7383 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007384
7385 // Create the (empty) function replacing the function on the stack frame for
7386 // the purpose of evaluating in the context created below. It is important
7387 // that this function does not describe any parameters and local variables
7388 // in the context. If it does then this will cause problems with the lookup
7389 // in Context::Lookup, where context slots for parameters and local variables
7390 // are looked at before the extension object.
7391 Handle<JSFunction> go_between =
7392 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7393 go_between->set_context(function->context());
7394#ifdef DEBUG
7395 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7396 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7397 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7398#endif
7399
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007400 // Materialize the content of the local scope into a JSObject.
7401 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007402
7403 // Allocate a new context for the debug evaluation and set the extension
7404 // object build.
7405 Handle<Context> context =
7406 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007407 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007408 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007409 Handle<Context> frame_context(Context::cast(frame->context()));
7410 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007411 context = CopyWithContextChain(frame_context, context);
7412
7413 // Wrap the evaluation statement in a new function compiled in the newly
7414 // created context. The function has one parameter which has to be called
7415 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007416 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007417 // function(arguments,__source__) {return eval(__source__);}
7418 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007419 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007420 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007421 Handle<String> function_source =
7422 Factory::NewStringFromAscii(Vector<const char>(source_str,
7423 source_str_length));
7424 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007425 Compiler::CompileEval(function_source,
7426 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007427 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007428 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007429 if (boilerplate.is_null()) return Failure::Exception();
7430 Handle<JSFunction> compiled_function =
7431 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7432
7433 // Invoke the result of the compilation to get the evaluation function.
7434 bool has_pending_exception;
7435 Handle<Object> receiver(frame->receiver());
7436 Handle<Object> evaluation_function =
7437 Execution::Call(compiled_function, receiver, 0, NULL,
7438 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007439 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007440
7441 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7442 function_context);
7443
7444 // Invoke the evaluation function and return the result.
7445 const int argc = 2;
7446 Object** argv[argc] = { arguments.location(),
7447 Handle<Object>::cast(source).location() };
7448 Handle<Object> result =
7449 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7450 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007451 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007452
7453 // Skip the global proxy as it has no properties and always delegates to the
7454 // real global object.
7455 if (result->IsJSGlobalProxy()) {
7456 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7457 }
7458
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007459 return *result;
7460}
7461
7462
7463static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7464 HandleScope scope;
7465
7466 // Check the execution state and decode arguments frame and source to be
7467 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007468 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007469 Object* check_result = Runtime_CheckExecutionState(args);
7470 if (check_result->IsFailure()) return check_result;
7471 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007472 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7473
7474 // Handle the processing of break.
7475 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007476
7477 // Enter the top context from before the debugger was invoked.
7478 SaveContext save;
7479 SaveContext* top = &save;
7480 while (top != NULL && *top->context() == *Debug::debug_context()) {
7481 top = top->prev();
7482 }
7483 if (top != NULL) {
7484 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007485 }
7486
7487 // Get the global context now set to the top context from before the
7488 // debugger was invoked.
7489 Handle<Context> context = Top::global_context();
7490
7491 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007492 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007493 Handle<JSFunction>(Compiler::CompileEval(source,
7494 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007495 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007496 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007497 if (boilerplate.is_null()) return Failure::Exception();
7498 Handle<JSFunction> compiled_function =
7499 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7500 context));
7501
7502 // Invoke the result of the compilation to get the evaluation function.
7503 bool has_pending_exception;
7504 Handle<Object> receiver = Top::global();
7505 Handle<Object> result =
7506 Execution::Call(compiled_function, receiver, 0, NULL,
7507 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007508 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007509 return *result;
7510}
7511
7512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007513static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7514 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007515 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007516
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007517 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007518 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007519
7520 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007521 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007522 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7523 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7524 // because using
7525 // instances->set(i, *GetScriptWrapper(script))
7526 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7527 // already have deferenced the instances handle.
7528 Handle<JSValue> wrapper = GetScriptWrapper(script);
7529 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007530 }
7531
7532 // Return result as a JS array.
7533 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7534 Handle<JSArray>::cast(result)->SetContent(*instances);
7535 return *result;
7536}
7537
7538
7539// Helper function used by Runtime_DebugReferencedBy below.
7540static int DebugReferencedBy(JSObject* target,
7541 Object* instance_filter, int max_references,
7542 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007543 JSFunction* arguments_function) {
7544 NoHandleAllocation ha;
7545 AssertNoAllocation no_alloc;
7546
7547 // Iterate the heap.
7548 int count = 0;
7549 JSObject* last = NULL;
7550 HeapIterator iterator;
7551 while (iterator.has_next() &&
7552 (max_references == 0 || count < max_references)) {
7553 // Only look at all JSObjects.
7554 HeapObject* heap_obj = iterator.next();
7555 if (heap_obj->IsJSObject()) {
7556 // Skip context extension objects and argument arrays as these are
7557 // checked in the context of functions using them.
7558 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007559 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007560 obj->map()->constructor() == arguments_function) {
7561 continue;
7562 }
7563
7564 // Check if the JS object has a reference to the object looked for.
7565 if (obj->ReferencesObject(target)) {
7566 // Check instance filter if supplied. This is normally used to avoid
7567 // references from mirror objects (see Runtime_IsInPrototypeChain).
7568 if (!instance_filter->IsUndefined()) {
7569 Object* V = obj;
7570 while (true) {
7571 Object* prototype = V->GetPrototype();
7572 if (prototype->IsNull()) {
7573 break;
7574 }
7575 if (instance_filter == prototype) {
7576 obj = NULL; // Don't add this object.
7577 break;
7578 }
7579 V = prototype;
7580 }
7581 }
7582
7583 if (obj != NULL) {
7584 // Valid reference found add to instance array if supplied an update
7585 // count.
7586 if (instances != NULL && count < instances_size) {
7587 instances->set(count, obj);
7588 }
7589 last = obj;
7590 count++;
7591 }
7592 }
7593 }
7594 }
7595
7596 // Check for circular reference only. This can happen when the object is only
7597 // referenced from mirrors and has a circular reference in which case the
7598 // object is not really alive and would have been garbage collected if not
7599 // referenced from the mirror.
7600 if (count == 1 && last == target) {
7601 count = 0;
7602 }
7603
7604 // Return the number of referencing objects found.
7605 return count;
7606}
7607
7608
7609// Scan the heap for objects with direct references to an object
7610// args[0]: the object to find references to
7611// args[1]: constructor function for instances to exclude (Mirror)
7612// args[2]: the the maximum number of objects to return
7613static Object* Runtime_DebugReferencedBy(Arguments args) {
7614 ASSERT(args.length() == 3);
7615
7616 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007617 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007618
7619 // Check parameters.
7620 CONVERT_CHECKED(JSObject, target, args[0]);
7621 Object* instance_filter = args[1];
7622 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7623 instance_filter->IsJSObject());
7624 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7625 RUNTIME_ASSERT(max_references >= 0);
7626
7627 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007628 JSObject* arguments_boilerplate =
7629 Top::context()->global_context()->arguments_boilerplate();
7630 JSFunction* arguments_function =
7631 JSFunction::cast(arguments_boilerplate->map()->constructor());
7632
7633 // Get the number of referencing objects.
7634 int count;
7635 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007636 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007637
7638 // Allocate an array to hold the result.
7639 Object* object = Heap::AllocateFixedArray(count);
7640 if (object->IsFailure()) return object;
7641 FixedArray* instances = FixedArray::cast(object);
7642
7643 // Fill the referencing objects.
7644 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007645 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007646
7647 // Return result as JS array.
7648 Object* result =
7649 Heap::AllocateJSObject(
7650 Top::context()->global_context()->array_function());
7651 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7652 return result;
7653}
7654
7655
7656// Helper function used by Runtime_DebugConstructedBy below.
7657static int DebugConstructedBy(JSFunction* constructor, int max_references,
7658 FixedArray* instances, int instances_size) {
7659 AssertNoAllocation no_alloc;
7660
7661 // Iterate the heap.
7662 int count = 0;
7663 HeapIterator iterator;
7664 while (iterator.has_next() &&
7665 (max_references == 0 || count < max_references)) {
7666 // Only look at all JSObjects.
7667 HeapObject* heap_obj = iterator.next();
7668 if (heap_obj->IsJSObject()) {
7669 JSObject* obj = JSObject::cast(heap_obj);
7670 if (obj->map()->constructor() == constructor) {
7671 // Valid reference found add to instance array if supplied an update
7672 // count.
7673 if (instances != NULL && count < instances_size) {
7674 instances->set(count, obj);
7675 }
7676 count++;
7677 }
7678 }
7679 }
7680
7681 // Return the number of referencing objects found.
7682 return count;
7683}
7684
7685
7686// Scan the heap for objects constructed by a specific function.
7687// args[0]: the constructor to find instances of
7688// args[1]: the the maximum number of objects to return
7689static Object* Runtime_DebugConstructedBy(Arguments args) {
7690 ASSERT(args.length() == 2);
7691
7692 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007693 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007694
7695 // Check parameters.
7696 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7697 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7698 RUNTIME_ASSERT(max_references >= 0);
7699
7700 // Get the number of referencing objects.
7701 int count;
7702 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7703
7704 // Allocate an array to hold the result.
7705 Object* object = Heap::AllocateFixedArray(count);
7706 if (object->IsFailure()) return object;
7707 FixedArray* instances = FixedArray::cast(object);
7708
7709 // Fill the referencing objects.
7710 count = DebugConstructedBy(constructor, max_references, instances, count);
7711
7712 // Return result as JS array.
7713 Object* result =
7714 Heap::AllocateJSObject(
7715 Top::context()->global_context()->array_function());
7716 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7717 return result;
7718}
7719
7720
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007721// Find the effective prototype object as returned by __proto__.
7722// args[0]: the object to find the prototype for.
7723static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007724 ASSERT(args.length() == 1);
7725
7726 CONVERT_CHECKED(JSObject, obj, args[0]);
7727
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007728 // Use the __proto__ accessor.
7729 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007730}
7731
7732
7733static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007734 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007735 CPU::DebugBreak();
7736 return Heap::undefined_value();
7737}
7738
7739
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007740static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007741#ifdef DEBUG
7742 HandleScope scope;
7743 ASSERT(args.length() == 1);
7744 // Get the function and make sure it is compiled.
7745 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7746 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7747 return Failure::Exception();
7748 }
7749 func->code()->PrintLn();
7750#endif // DEBUG
7751 return Heap::undefined_value();
7752}
ager@chromium.org9085a012009-05-11 19:22:57 +00007753
7754
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007755static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7756#ifdef DEBUG
7757 HandleScope scope;
7758 ASSERT(args.length() == 1);
7759 // Get the function and make sure it is compiled.
7760 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7761 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7762 return Failure::Exception();
7763 }
7764 func->shared()->construct_stub()->PrintLn();
7765#endif // DEBUG
7766 return Heap::undefined_value();
7767}
7768
7769
ager@chromium.org9085a012009-05-11 19:22:57 +00007770static Object* Runtime_FunctionGetInferredName(Arguments args) {
7771 NoHandleAllocation ha;
7772 ASSERT(args.length() == 1);
7773
7774 CONVERT_CHECKED(JSFunction, f, args[0]);
7775 return f->shared()->inferred_name();
7776}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007777
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007778#endif // ENABLE_DEBUGGER_SUPPORT
7779
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007780#ifdef ENABLE_LOGGING_AND_PROFILING
7781
7782static Object* Runtime_ProfilerResume(Arguments args) {
7783 NoHandleAllocation ha;
7784 ASSERT(args.length() == 1);
7785
7786 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7787 v8::V8::ResumeProfilerEx(smi_modules->value());
7788 return Heap::undefined_value();
7789}
7790
7791
7792static Object* Runtime_ProfilerPause(Arguments args) {
7793 NoHandleAllocation ha;
7794 ASSERT(args.length() == 1);
7795
7796 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7797 v8::V8::PauseProfilerEx(smi_modules->value());
7798 return Heap::undefined_value();
7799}
7800
7801#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007802
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007803// Finds the script object from the script data. NOTE: This operation uses
7804// heap traversal to find the function generated for the source position
7805// for the requested break point. For lazily compiled functions several heap
7806// traversals might be required rendering this operation as a rather slow
7807// operation. However for setting break points which is normally done through
7808// some kind of user interaction the performance is not crucial.
7809static Handle<Object> Runtime_GetScriptFromScriptName(
7810 Handle<String> script_name) {
7811 // Scan the heap for Script objects to find the script with the requested
7812 // script data.
7813 Handle<Script> script;
7814 HeapIterator iterator;
7815 while (script.is_null() && iterator.has_next()) {
7816 HeapObject* obj = iterator.next();
7817 // If a script is found check if it has the script data requested.
7818 if (obj->IsScript()) {
7819 if (Script::cast(obj)->name()->IsString()) {
7820 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7821 script = Handle<Script>(Script::cast(obj));
7822 }
7823 }
7824 }
7825 }
7826
7827 // If no script with the requested script data is found return undefined.
7828 if (script.is_null()) return Factory::undefined_value();
7829
7830 // Return the script found.
7831 return GetScriptWrapper(script);
7832}
7833
7834
7835// Get the script object from script data. NOTE: Regarding performance
7836// see the NOTE for GetScriptFromScriptData.
7837// args[0]: script data for the script to find the source for
7838static Object* Runtime_GetScript(Arguments args) {
7839 HandleScope scope;
7840
7841 ASSERT(args.length() == 1);
7842
7843 CONVERT_CHECKED(String, script_name, args[0]);
7844
7845 // Find the requested script.
7846 Handle<Object> result =
7847 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7848 return *result;
7849}
7850
7851
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007852// Determines whether the given stack frame should be displayed in
7853// a stack trace. The caller is the error constructor that asked
7854// for the stack trace to be collected. The first time a construct
7855// call to this function is encountered it is skipped. The seen_caller
7856// in/out parameter is used to remember if the caller has been seen
7857// yet.
7858static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7859 bool* seen_caller) {
7860 // Only display JS frames.
7861 if (!raw_frame->is_java_script())
7862 return false;
7863 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7864 Object* raw_fun = frame->function();
7865 // Not sure when this can happen but skip it just in case.
7866 if (!raw_fun->IsJSFunction())
7867 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007868 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007869 *seen_caller = true;
7870 return false;
7871 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007872 // Skip all frames until we've seen the caller. Also, skip the most
7873 // obvious builtin calls. Some builtin calls (such as Number.ADD
7874 // which is invoked using 'call') are very difficult to recognize
7875 // so we're leaving them in for now.
7876 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007877}
7878
7879
7880// Collect the raw data for a stack trace. Returns an array of three
7881// element segments each containing a receiver, function and native
7882// code offset.
7883static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007884 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007885 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007886 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7887
7888 HandleScope scope;
7889
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007890 limit = Max(limit, 0); // Ensure that limit is not negative.
7891 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007892 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007893
7894 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007895 // If the caller parameter is a function we skip frames until we're
7896 // under it before starting to collect.
7897 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007898 int cursor = 0;
7899 int frames_seen = 0;
7900 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007901 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007902 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007903 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007904 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007905 Object* recv = frame->receiver();
7906 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007907 Address pc = frame->pc();
7908 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007909 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007910 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007911 if (cursor + 2 < elements->length()) {
7912 elements->set(cursor++, recv);
7913 elements->set(cursor++, fun);
7914 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7915 } else {
7916 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007917 Handle<Object> recv_handle(recv);
7918 Handle<Object> fun_handle(fun);
7919 SetElement(result, cursor++, recv_handle);
7920 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007921 SetElement(result, cursor++, Handle<Smi>(offset));
7922 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007923 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007924 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007925 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007926
7927 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7928
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007929 return *result;
7930}
7931
7932
ager@chromium.org3811b432009-10-28 14:53:37 +00007933// Returns V8 version as a string.
7934static Object* Runtime_GetV8Version(Arguments args) {
7935 ASSERT_EQ(args.length(), 0);
7936
7937 NoHandleAllocation ha;
7938
7939 const char* version_string = v8::V8::GetVersion();
7940
7941 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
7942}
7943
7944
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007945static Object* Runtime_Abort(Arguments args) {
7946 ASSERT(args.length() == 2);
7947 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7948 Smi::cast(args[1])->value());
7949 Top::PrintStack();
7950 OS::Abort();
7951 UNREACHABLE();
7952 return NULL;
7953}
7954
7955
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007956static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
7957 ASSERT(args.length() == 0);
7958 HandleScope::DeleteExtensions();
7959 return Heap::undefined_value();
7960}
7961
7962
kasper.lund44510672008-07-25 07:37:58 +00007963#ifdef DEBUG
7964// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7965// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007966static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007967 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007968 HandleScope scope;
7969 Handle<JSArray> result = Factory::NewJSArray(0);
7970 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007971#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007972 { \
7973 HandleScope inner; \
7974 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007975 Factory::NewStringFromAscii( \
7976 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007977 Handle<JSArray> pair = Factory::NewJSArray(0); \
7978 SetElement(pair, 0, name); \
7979 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7980 SetElement(result, index++, pair); \
7981 }
7982 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7983#undef ADD_ENTRY
7984 return *result;
7985}
kasper.lund44510672008-07-25 07:37:58 +00007986#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007987
7988
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007989static Object* Runtime_Log(Arguments args) {
7990 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007991 CONVERT_CHECKED(String, format, args[0]);
7992 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007993 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007994 Logger::LogRuntime(chars, elms);
7995 return Heap::undefined_value();
7996}
7997
7998
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007999static Object* Runtime_IS_VAR(Arguments args) {
8000 UNREACHABLE(); // implemented as macro in the parser
8001 return NULL;
8002}
8003
8004
8005// ----------------------------------------------------------------------------
8006// Implementation of Runtime
8007
ager@chromium.orga1645e22009-09-09 19:27:10 +00008008#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008009 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00008010 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008011
8012static Runtime::Function Runtime_functions[] = {
8013 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00008014 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008015};
8016
8017#undef F
8018
8019
8020Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
8021 ASSERT(0 <= fid && fid < kNofFunctions);
8022 return &Runtime_functions[fid];
8023}
8024
8025
8026Runtime::Function* Runtime::FunctionForName(const char* name) {
8027 for (Function* f = Runtime_functions; f->name != NULL; f++) {
8028 if (strcmp(f->name, name) == 0) {
8029 return f;
8030 }
8031 }
8032 return NULL;
8033}
8034
8035
8036void Runtime::PerformGC(Object* result) {
8037 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008038 if (failure->IsRetryAfterGC()) {
8039 // Try to do a garbage collection; ignore it if it fails. The C
8040 // entry stub will throw an out-of-memory exception in that case.
8041 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
8042 } else {
8043 // Handle last resort GC and make sure to allow future allocations
8044 // to grow the heap without causing GCs (if possible).
8045 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008046 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008047 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008048}
8049
8050
8051} } // namespace v8::internal