blob: ac61de202ad595fdc49df1f989b13d39cbfb8717 [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 =
723 Factory::NewFunctionFromBoilerplate(boilerplate, context);
724 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
4505 Handle<JSFunction> result =
4506 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4507 return *result;
4508}
4509
4510
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004511static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004512 // TODO(385): Change this to create a construct stub specialized for
4513 // the given map to make allocation of simple objects - and maybe
4514 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004515 if (FLAG_inline_new
4516 && shared->has_only_simple_this_property_assignments()) {
4517 ConstructStubCompiler compiler;
4518 Object* code = compiler.CompileConstructStub(*shared);
4519 if (code->IsFailure()) {
4520 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4521 }
4522 return Code::cast(code);
4523 }
4524
4525 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004526}
4527
4528
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004529static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004530 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004531 ASSERT(args.length() == 1);
4532
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004533 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004534
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004535 // If the constructor isn't a proper function we throw a type error.
4536 if (!constructor->IsJSFunction()) {
4537 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4538 Handle<Object> type_error =
4539 Factory::NewTypeError("not_constructor", arguments);
4540 return Top::Throw(*type_error);
4541 }
4542
4543 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004544#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004545 // Handle stepping into constructors if step into is active.
4546 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004547 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004548 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004549#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004550
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004551 if (function->has_initial_map()) {
4552 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004553 // The 'Function' function ignores the receiver object when
4554 // called using 'new' and creates a new JSFunction object that
4555 // is returned. The receiver object is only used for error
4556 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004557 // JSFunction. Factory::NewJSObject() should not be used to
4558 // allocate JSFunctions since it does not properly initialize
4559 // the shared part of the function. Since the receiver is
4560 // ignored anyway, we use the global object as the receiver
4561 // instead of a new JSFunction object. This way, errors are
4562 // reported the same way whether or not 'Function' is called
4563 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004564 return Top::context()->global();
4565 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004566 }
4567
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004568 // The function should be compiled for the optimization hints to be available.
4569 if (!function->shared()->is_compiled()) {
4570 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4571 CLEAR_EXCEPTION,
4572 0);
4573 }
4574
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004575 bool first_allocation = !function->has_initial_map();
4576 Handle<JSObject> result = Factory::NewJSObject(function);
4577 if (first_allocation) {
4578 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004579 Handle<Code> stub = Handle<Code>(
4580 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004581 function->shared()->set_construct_stub(*stub);
4582 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004583
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004584 Counters::constructed_objects.Increment();
4585 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004586
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004587 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004588}
4589
4590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591static Object* Runtime_LazyCompile(Arguments args) {
4592 HandleScope scope;
4593 ASSERT(args.length() == 1);
4594
4595 Handle<JSFunction> function = args.at<JSFunction>(0);
4596#ifdef DEBUG
4597 if (FLAG_trace_lazy) {
4598 PrintF("[lazy: ");
4599 function->shared()->name()->Print();
4600 PrintF("]\n");
4601 }
4602#endif
4603
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004604 // Compile the target function. Here we compile using CompileLazyInLoop in
4605 // order to get the optimized version. This helps code like delta-blue
4606 // that calls performance-critical routines through constructors. A
4607 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4608 // direct call. Since the in-loop tracking takes place through CallICs
4609 // this means that things called through constructors are never known to
4610 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004611 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004612 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004613 return Failure::Exception();
4614 }
4615
4616 return function->code();
4617}
4618
4619
4620static Object* Runtime_GetCalledFunction(Arguments args) {
4621 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004622 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004623 StackFrameIterator it;
4624 // Get past the JS-to-C exit frame.
4625 ASSERT(it.frame()->is_exit());
4626 it.Advance();
4627 // Get past the CALL_NON_FUNCTION activation frame.
4628 ASSERT(it.frame()->is_java_script());
4629 it.Advance();
4630 // Argument adaptor frames do not copy the function; we have to skip
4631 // past them to get to the real calling frame.
4632 if (it.frame()->is_arguments_adaptor()) it.Advance();
4633 // Get the function from the top of the expression stack of the
4634 // calling frame.
4635 StandardFrame* frame = StandardFrame::cast(it.frame());
4636 int index = frame->ComputeExpressionsCount() - 1;
4637 Object* result = frame->GetExpression(index);
4638 return result;
4639}
4640
4641
4642static Object* Runtime_GetFunctionDelegate(Arguments args) {
4643 HandleScope scope;
4644 ASSERT(args.length() == 1);
4645 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4646 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4647}
4648
4649
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004650static Object* Runtime_GetConstructorDelegate(Arguments args) {
4651 HandleScope scope;
4652 ASSERT(args.length() == 1);
4653 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4654 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4655}
4656
4657
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004658static Object* Runtime_NewContext(Arguments args) {
4659 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004660 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004661
kasper.lund7276f142008-07-30 08:49:36 +00004662 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004663 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4664 Object* result = Heap::AllocateFunctionContext(length, function);
4665 if (result->IsFailure()) return result;
4666
4667 Top::set_context(Context::cast(result));
4668
kasper.lund7276f142008-07-30 08:49:36 +00004669 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004670}
4671
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004672static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004673 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004674 Object* js_object = object;
4675 if (!js_object->IsJSObject()) {
4676 js_object = js_object->ToObject();
4677 if (js_object->IsFailure()) {
4678 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004679 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004680 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004681 Handle<Object> result =
4682 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4683 return Top::Throw(*result);
4684 }
4685 }
4686
4687 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004688 Heap::AllocateWithContext(Top::context(),
4689 JSObject::cast(js_object),
4690 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691 if (result->IsFailure()) return result;
4692
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004693 Context* context = Context::cast(result);
4694 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004695
kasper.lund7276f142008-07-30 08:49:36 +00004696 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004697}
4698
4699
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004700static Object* Runtime_PushContext(Arguments args) {
4701 NoHandleAllocation ha;
4702 ASSERT(args.length() == 1);
4703 return PushContextHelper(args[0], false);
4704}
4705
4706
4707static Object* Runtime_PushCatchContext(Arguments args) {
4708 NoHandleAllocation ha;
4709 ASSERT(args.length() == 1);
4710 return PushContextHelper(args[0], true);
4711}
4712
4713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004714static Object* Runtime_LookupContext(Arguments args) {
4715 HandleScope scope;
4716 ASSERT(args.length() == 2);
4717
4718 CONVERT_ARG_CHECKED(Context, context, 0);
4719 CONVERT_ARG_CHECKED(String, name, 1);
4720
4721 int index;
4722 PropertyAttributes attributes;
4723 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004724 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004725 context->Lookup(name, flags, &index, &attributes);
4726
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004727 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004728 ASSERT(holder->IsJSObject());
4729 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004730 }
4731
4732 // No intermediate context found. Use global object by default.
4733 return Top::context()->global();
4734}
4735
4736
ager@chromium.orga1645e22009-09-09 19:27:10 +00004737// A mechanism to return a pair of Object pointers in registers (if possible).
4738// How this is achieved is calling convention-dependent.
4739// All currently supported x86 compiles uses calling conventions that are cdecl
4740// variants where a 64-bit value is returned in two 32-bit registers
4741// (edx:eax on ia32, r1:r0 on ARM).
4742// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4743// In Win64 calling convention, a struct of two pointers is returned in memory,
4744// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004745#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004746struct ObjectPair {
4747 Object* x;
4748 Object* y;
4749};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004750
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004751static inline ObjectPair MakePair(Object* x, Object* y) {
4752 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004753 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4754 // In Win64 they are assigned to a hidden first argument.
4755 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004756}
4757#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004758typedef uint64_t ObjectPair;
4759static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004760 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004761 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004762}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004763#endif
4764
4765
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004766static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004767 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4768 USE(attributes);
4769 return x->IsTheHole() ? Heap::undefined_value() : x;
4770}
4771
4772
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004773static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4774 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004775 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004776 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004777 JSFunction* context_extension_function =
4778 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004779 // If the holder isn't a context extension object, we just return it
4780 // as the receiver. This allows arguments objects to be used as
4781 // receivers, but only if they are put in the context scope chain
4782 // explicitly via a with-statement.
4783 Object* constructor = holder->map()->constructor();
4784 if (constructor != context_extension_function) return holder;
4785 // Fall back to using the global object as the receiver if the
4786 // property turns out to be a local variable allocated in a context
4787 // extension object - introduced via eval.
4788 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004789}
4790
4791
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004792static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004794 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004795
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004796 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004797 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004798 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004799 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004800 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004801
4802 int index;
4803 PropertyAttributes attributes;
4804 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004805 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004806 context->Lookup(name, flags, &index, &attributes);
4807
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004808 // If the index is non-negative, the slot has been found in a local
4809 // variable or a parameter. Read it from the context object or the
4810 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004811 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004812 // If the "property" we were looking for is a local variable or an
4813 // argument in a context, the receiver is the global object; see
4814 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4815 JSObject* receiver = Top::context()->global()->global_receiver();
4816 Object* value = (holder->IsContext())
4817 ? Context::cast(*holder)->get(index)
4818 : JSObject::cast(*holder)->GetElement(index);
4819 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820 }
4821
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004822 // If the holder is found, we read the property from it.
4823 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004824 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004825 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004826 JSObject* receiver;
4827 if (object->IsGlobalObject()) {
4828 receiver = GlobalObject::cast(object)->global_receiver();
4829 } else if (context->is_exception_holder(*holder)) {
4830 receiver = Top::context()->global()->global_receiver();
4831 } else {
4832 receiver = ComputeReceiverForNonGlobal(object);
4833 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004834 // No need to unhole the value here. This is taken care of by the
4835 // GetProperty function.
4836 Object* value = object->GetProperty(*name);
4837 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004838 }
4839
4840 if (throw_error) {
4841 // The property doesn't exist - throw exception.
4842 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004843 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004844 return MakePair(Top::Throw(*reference_error), NULL);
4845 } else {
4846 // The property doesn't exist - return undefined
4847 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4848 }
4849}
4850
4851
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004852static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853 return LoadContextSlotHelper(args, true);
4854}
4855
4856
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004857static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004858 return LoadContextSlotHelper(args, false);
4859}
4860
4861
4862static Object* Runtime_StoreContextSlot(Arguments args) {
4863 HandleScope scope;
4864 ASSERT(args.length() == 3);
4865
4866 Handle<Object> value(args[0]);
4867 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004868 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004869
4870 int index;
4871 PropertyAttributes attributes;
4872 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004873 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004874 context->Lookup(name, flags, &index, &attributes);
4875
4876 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004877 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878 // Ignore if read_only variable.
4879 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004880 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004881 }
4882 } else {
4883 ASSERT((attributes & READ_ONLY) == 0);
4884 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004885 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004886 USE(result);
4887 ASSERT(!result->IsFailure());
4888 }
4889 return *value;
4890 }
4891
4892 // Slow case: The property is not in a FixedArray context.
4893 // It is either in an JSObject extension context or it was not found.
4894 Handle<JSObject> context_ext;
4895
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004896 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004897 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004898 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 } else {
4900 // The property was not found. It needs to be stored in the global context.
4901 ASSERT(attributes == ABSENT);
4902 attributes = NONE;
4903 context_ext = Handle<JSObject>(Top::context()->global());
4904 }
4905
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004906 // Set the property, but ignore if read_only variable on the context
4907 // extension object itself.
4908 if ((attributes & READ_ONLY) == 0 ||
4909 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004910 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4911 if (set.is_null()) {
4912 // Failure::Exception is converted to a null handle in the
4913 // handle-based methods such as SetProperty. We therefore need
4914 // to convert null handles back to exceptions.
4915 ASSERT(Top::has_pending_exception());
4916 return Failure::Exception();
4917 }
4918 }
4919 return *value;
4920}
4921
4922
4923static Object* Runtime_Throw(Arguments args) {
4924 HandleScope scope;
4925 ASSERT(args.length() == 1);
4926
4927 return Top::Throw(args[0]);
4928}
4929
4930
4931static Object* Runtime_ReThrow(Arguments args) {
4932 HandleScope scope;
4933 ASSERT(args.length() == 1);
4934
4935 return Top::ReThrow(args[0]);
4936}
4937
4938
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004939static Object* Runtime_PromoteScheduledException(Arguments args) {
4940 ASSERT_EQ(0, args.length());
4941 return Top::PromoteScheduledException();
4942}
4943
4944
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004945static Object* Runtime_ThrowReferenceError(Arguments args) {
4946 HandleScope scope;
4947 ASSERT(args.length() == 1);
4948
4949 Handle<Object> name(args[0]);
4950 Handle<Object> reference_error =
4951 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4952 return Top::Throw(*reference_error);
4953}
4954
4955
4956static Object* Runtime_StackOverflow(Arguments args) {
4957 NoHandleAllocation na;
4958 return Top::StackOverflow();
4959}
4960
4961
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004962static Object* Runtime_StackGuard(Arguments args) {
4963 ASSERT(args.length() == 1);
4964
4965 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004966 if (StackGuard::IsStackOverflow()) {
4967 return Runtime_StackOverflow(args);
4968 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004969
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004970 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004971}
4972
4973
4974// NOTE: These PrintXXX functions are defined for all builds (not just
4975// DEBUG builds) because we may want to be able to trace function
4976// calls in all modes.
4977static void PrintString(String* str) {
4978 // not uncommon to have empty strings
4979 if (str->length() > 0) {
4980 SmartPointer<char> s =
4981 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4982 PrintF("%s", *s);
4983 }
4984}
4985
4986
4987static void PrintObject(Object* obj) {
4988 if (obj->IsSmi()) {
4989 PrintF("%d", Smi::cast(obj)->value());
4990 } else if (obj->IsString() || obj->IsSymbol()) {
4991 PrintString(String::cast(obj));
4992 } else if (obj->IsNumber()) {
4993 PrintF("%g", obj->Number());
4994 } else if (obj->IsFailure()) {
4995 PrintF("<failure>");
4996 } else if (obj->IsUndefined()) {
4997 PrintF("<undefined>");
4998 } else if (obj->IsNull()) {
4999 PrintF("<null>");
5000 } else if (obj->IsTrue()) {
5001 PrintF("<true>");
5002 } else if (obj->IsFalse()) {
5003 PrintF("<false>");
5004 } else {
5005 PrintF("%p", obj);
5006 }
5007}
5008
5009
5010static int StackSize() {
5011 int n = 0;
5012 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
5013 return n;
5014}
5015
5016
5017static void PrintTransition(Object* result) {
5018 // indentation
5019 { const int nmax = 80;
5020 int n = StackSize();
5021 if (n <= nmax)
5022 PrintF("%4d:%*s", n, n, "");
5023 else
5024 PrintF("%4d:%*s", n, nmax, "...");
5025 }
5026
5027 if (result == NULL) {
5028 // constructor calls
5029 JavaScriptFrameIterator it;
5030 JavaScriptFrame* frame = it.frame();
5031 if (frame->IsConstructor()) PrintF("new ");
5032 // function name
5033 Object* fun = frame->function();
5034 if (fun->IsJSFunction()) {
5035 PrintObject(JSFunction::cast(fun)->shared()->name());
5036 } else {
5037 PrintObject(fun);
5038 }
5039 // function arguments
5040 // (we are intentionally only printing the actually
5041 // supplied parameters, not all parameters required)
5042 PrintF("(this=");
5043 PrintObject(frame->receiver());
5044 const int length = frame->GetProvidedParametersCount();
5045 for (int i = 0; i < length; i++) {
5046 PrintF(", ");
5047 PrintObject(frame->GetParameter(i));
5048 }
5049 PrintF(") {\n");
5050
5051 } else {
5052 // function result
5053 PrintF("} -> ");
5054 PrintObject(result);
5055 PrintF("\n");
5056 }
5057}
5058
5059
5060static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005061 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005062 NoHandleAllocation ha;
5063 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005064 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005065}
5066
5067
5068static Object* Runtime_TraceExit(Arguments args) {
5069 NoHandleAllocation ha;
5070 PrintTransition(args[0]);
5071 return args[0]; // return TOS
5072}
5073
5074
5075static Object* Runtime_DebugPrint(Arguments args) {
5076 NoHandleAllocation ha;
5077 ASSERT(args.length() == 1);
5078
5079#ifdef DEBUG
5080 if (args[0]->IsString()) {
5081 // If we have a string, assume it's a code "marker"
5082 // and print some interesting cpu debugging info.
5083 JavaScriptFrameIterator it;
5084 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005085 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
5086 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005087 } else {
5088 PrintF("DebugPrint: ");
5089 }
5090 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005091 if (args[0]->IsHeapObject()) {
5092 HeapObject::cast(args[0])->map()->Print();
5093 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005094#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005095 // ShortPrint is available in release mode. Print is not.
5096 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005097#endif
5098 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00005099 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005100
5101 return args[0]; // return TOS
5102}
5103
5104
5105static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005106 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005107 NoHandleAllocation ha;
5108 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005109 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005110}
5111
5112
mads.s.ager31e71382008-08-13 09:32:07 +00005113static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005114 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005115 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005116
5117 // According to ECMA-262, section 15.9.1, page 117, the precision of
5118 // the number in a Date object representing a particular instant in
5119 // time is milliseconds. Therefore, we floor the result of getting
5120 // the OS time.
5121 double millis = floor(OS::TimeCurrentMillis());
5122 return Heap::NumberFromDouble(millis);
5123}
5124
5125
5126static Object* Runtime_DateParseString(Arguments args) {
5127 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005128 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005129
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005130 CONVERT_ARG_CHECKED(String, str, 0);
5131 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005132
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005133 CONVERT_ARG_CHECKED(JSArray, output, 1);
5134 RUNTIME_ASSERT(output->HasFastElements());
5135
5136 AssertNoAllocation no_allocation;
5137
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005138 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005139 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5140 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005141 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005142 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005143 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005144 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005145 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5146 }
5147
5148 if (result) {
5149 return *output;
5150 } else {
5151 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005152 }
5153}
5154
5155
5156static Object* Runtime_DateLocalTimezone(Arguments args) {
5157 NoHandleAllocation ha;
5158 ASSERT(args.length() == 1);
5159
5160 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005161 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005162 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5163}
5164
5165
5166static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5167 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005168 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005169
5170 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5171}
5172
5173
5174static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5175 NoHandleAllocation ha;
5176 ASSERT(args.length() == 1);
5177
5178 CONVERT_DOUBLE_CHECKED(x, args[0]);
5179 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5180}
5181
5182
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005183static Object* Runtime_NumberIsFinite(Arguments args) {
5184 NoHandleAllocation ha;
5185 ASSERT(args.length() == 1);
5186
5187 CONVERT_DOUBLE_CHECKED(value, args[0]);
5188 Object* result;
5189 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5190 result = Heap::false_value();
5191 } else {
5192 result = Heap::true_value();
5193 }
5194 return result;
5195}
5196
5197
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005198static Object* Runtime_GlobalReceiver(Arguments args) {
5199 ASSERT(args.length() == 1);
5200 Object* global = args[0];
5201 if (!global->IsJSGlobalObject()) return Heap::null_value();
5202 return JSGlobalObject::cast(global)->global_receiver();
5203}
5204
5205
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005206static Object* Runtime_CompileString(Arguments args) {
5207 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005208 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005209 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005210 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005211
ager@chromium.org381abbb2009-02-25 13:23:22 +00005212 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005213 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005214 Compiler::ValidationState validate = (is_json->IsTrue())
5215 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005216 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5217 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005218 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005219 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005220 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005221 Handle<JSFunction> fun =
5222 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5223 return *fun;
5224}
5225
5226
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005227static Handle<JSFunction> GetBuiltinFunction(String* name) {
5228 LookupResult result;
5229 Top::global_context()->builtins()->LocalLookup(name, &result);
5230 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5231}
5232
5233
5234static Object* CompileDirectEval(Handle<String> source) {
5235 // Compute the eval context.
5236 HandleScope scope;
5237 StackFrameLocator locator;
5238 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5239 Handle<Context> context(Context::cast(frame->context()));
5240 bool is_global = context->IsGlobalContext();
5241
ager@chromium.org381abbb2009-02-25 13:23:22 +00005242 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005243 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5244 source,
5245 context,
5246 is_global,
5247 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005248 if (boilerplate.is_null()) return Failure::Exception();
5249 Handle<JSFunction> fun =
5250 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5251 return *fun;
5252}
5253
5254
5255static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5256 ASSERT(args.length() == 2);
5257
5258 HandleScope scope;
5259
5260 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5261
5262 Handle<Object> receiver;
5263
5264 // Find where the 'eval' symbol is bound. It is unaliased only if
5265 // it is bound in the global context.
5266 StackFrameLocator locator;
5267 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5268 Handle<Context> context(Context::cast(frame->context()));
5269 int index;
5270 PropertyAttributes attributes;
5271 while (!context.is_null()) {
5272 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5273 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005274 // Stop search when eval is found or when the global context is
5275 // reached.
5276 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005277 if (context->is_function_context()) {
5278 context = Handle<Context>(Context::cast(context->closure()->context()));
5279 } else {
5280 context = Handle<Context>(context->previous());
5281 }
5282 }
5283
iposva@chromium.org245aa852009-02-10 00:49:54 +00005284 // If eval could not be resolved, it has been deleted and we need to
5285 // throw a reference error.
5286 if (attributes == ABSENT) {
5287 Handle<Object> name = Factory::eval_symbol();
5288 Handle<Object> reference_error =
5289 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5290 return Top::Throw(*reference_error);
5291 }
5292
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005293 if (context->IsGlobalContext()) {
5294 // 'eval' is bound in the global context, but it may have been overwritten.
5295 // Compare it to the builtin 'GlobalEval' function to make sure.
5296 Handle<JSFunction> global_eval =
5297 GetBuiltinFunction(Heap::global_eval_symbol());
5298 if (global_eval.is_identical_to(callee)) {
5299 // A direct eval call.
5300 if (args[1]->IsString()) {
5301 CONVERT_ARG_CHECKED(String, source, 1);
5302 // A normal eval call on a string. Compile it and return the
5303 // compiled function bound in the local context.
5304 Object* compiled_source = CompileDirectEval(source);
5305 if (compiled_source->IsFailure()) return compiled_source;
5306 receiver = Handle<Object>(frame->receiver());
5307 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5308 } else {
5309 // An eval call that is not called on a string. Global eval
5310 // deals better with this.
5311 receiver = Handle<Object>(Top::global_context()->global());
5312 }
5313 } else {
5314 // 'eval' is overwritten. Just call the function with the given arguments.
5315 receiver = Handle<Object>(Top::global_context()->global());
5316 }
5317 } else {
5318 // 'eval' is not bound in the global context. Just call the function
5319 // with the given arguments. This is not necessarily the global eval.
5320 if (receiver->IsContext()) {
5321 context = Handle<Context>::cast(receiver);
5322 receiver = Handle<Object>(context->get(index));
5323 }
5324 }
5325
5326 Handle<FixedArray> call = Factory::NewFixedArray(2);
5327 call->set(0, *callee);
5328 call->set(1, *receiver);
5329 return *call;
5330}
5331
5332
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005333static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5334 // This utility adjusts the property attributes for newly created Function
5335 // object ("new Function(...)") by changing the map.
5336 // All it does is changing the prototype property to enumerable
5337 // as specified in ECMA262, 15.3.5.2.
5338 HandleScope scope;
5339 ASSERT(args.length() == 1);
5340 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5341 ASSERT(func->map()->instance_type() ==
5342 Top::function_instance_map()->instance_type());
5343 ASSERT(func->map()->instance_size() ==
5344 Top::function_instance_map()->instance_size());
5345 func->set_map(*Top::function_instance_map());
5346 return *func;
5347}
5348
5349
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005350// Push an array unto an array of arrays if it is not already in the
5351// array. Returns true if the element was pushed on the stack and
5352// false otherwise.
5353static Object* Runtime_PushIfAbsent(Arguments args) {
5354 ASSERT(args.length() == 2);
5355 CONVERT_CHECKED(JSArray, array, args[0]);
5356 CONVERT_CHECKED(JSArray, element, args[1]);
5357 RUNTIME_ASSERT(array->HasFastElements());
5358 int length = Smi::cast(array->length())->value();
5359 FixedArray* elements = FixedArray::cast(array->elements());
5360 for (int i = 0; i < length; i++) {
5361 if (elements->get(i) == element) return Heap::false_value();
5362 }
5363 Object* obj = array->SetFastElement(length, element);
5364 if (obj->IsFailure()) return obj;
5365 return Heap::true_value();
5366}
5367
5368
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005369/**
5370 * A simple visitor visits every element of Array's.
5371 * The backend storage can be a fixed array for fast elements case,
5372 * or a dictionary for sparse array. Since Dictionary is a subtype
5373 * of FixedArray, the class can be used by both fast and slow cases.
5374 * The second parameter of the constructor, fast_elements, specifies
5375 * whether the storage is a FixedArray or Dictionary.
5376 *
5377 * An index limit is used to deal with the situation that a result array
5378 * length overflows 32-bit non-negative integer.
5379 */
5380class ArrayConcatVisitor {
5381 public:
5382 ArrayConcatVisitor(Handle<FixedArray> storage,
5383 uint32_t index_limit,
5384 bool fast_elements) :
5385 storage_(storage), index_limit_(index_limit),
5386 fast_elements_(fast_elements), index_offset_(0) { }
5387
5388 void visit(uint32_t i, Handle<Object> elm) {
5389 uint32_t index = i + index_offset_;
5390 if (index >= index_limit_) return;
5391
5392 if (fast_elements_) {
5393 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5394 storage_->set(index, *elm);
5395
5396 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005397 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5398 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005399 Factory::DictionaryAtNumberPut(dict, index, elm);
5400 if (!result.is_identical_to(dict))
5401 storage_ = result;
5402 }
5403 }
5404
5405 void increase_index_offset(uint32_t delta) {
5406 index_offset_ += delta;
5407 }
5408
5409 private:
5410 Handle<FixedArray> storage_;
5411 uint32_t index_limit_;
5412 bool fast_elements_;
5413 uint32_t index_offset_;
5414};
5415
5416
ager@chromium.org3811b432009-10-28 14:53:37 +00005417template<class ExternalArrayClass, class ElementType>
5418static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5419 bool elements_are_ints,
5420 bool elements_are_guaranteed_smis,
5421 uint32_t range,
5422 ArrayConcatVisitor* visitor) {
5423 Handle<ExternalArrayClass> array(
5424 ExternalArrayClass::cast(receiver->elements()));
5425 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5426
5427 if (visitor != NULL) {
5428 if (elements_are_ints) {
5429 if (elements_are_guaranteed_smis) {
5430 for (uint32_t j = 0; j < len; j++) {
5431 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5432 visitor->visit(j, e);
5433 }
5434 } else {
5435 for (uint32_t j = 0; j < len; j++) {
5436 int64_t val = static_cast<int64_t>(array->get(j));
5437 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5438 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5439 visitor->visit(j, e);
5440 } else {
5441 Handle<Object> e(
5442 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5443 visitor->visit(j, e);
5444 }
5445 }
5446 }
5447 } else {
5448 for (uint32_t j = 0; j < len; j++) {
5449 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5450 visitor->visit(j, e);
5451 }
5452 }
5453 }
5454
5455 return len;
5456}
5457
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005458/**
5459 * A helper function that visits elements of a JSObject. Only elements
5460 * whose index between 0 and range (exclusive) are visited.
5461 *
5462 * If the third parameter, visitor, is not NULL, the visitor is called
5463 * with parameters, 'visitor_index_offset + element index' and the element.
5464 *
5465 * It returns the number of visisted elements.
5466 */
5467static uint32_t IterateElements(Handle<JSObject> receiver,
5468 uint32_t range,
5469 ArrayConcatVisitor* visitor) {
5470 uint32_t num_of_elements = 0;
5471
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005472 switch (receiver->GetElementsKind()) {
5473 case JSObject::FAST_ELEMENTS: {
5474 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5475 uint32_t len = elements->length();
5476 if (range < len) {
5477 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005478 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005479
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005480 for (uint32_t j = 0; j < len; j++) {
5481 Handle<Object> e(elements->get(j));
5482 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005483 num_of_elements++;
5484 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005485 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005486 }
5487 }
5488 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005489 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005490 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005491 case JSObject::PIXEL_ELEMENTS: {
5492 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5493 uint32_t len = pixels->length();
5494 if (range < len) {
5495 len = range;
5496 }
5497
5498 for (uint32_t j = 0; j < len; j++) {
5499 num_of_elements++;
5500 if (visitor != NULL) {
5501 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5502 visitor->visit(j, e);
5503 }
5504 }
5505 break;
5506 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005507 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5508 num_of_elements =
5509 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5510 receiver, true, true, range, visitor);
5511 break;
5512 }
5513 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5514 num_of_elements =
5515 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5516 receiver, true, true, range, visitor);
5517 break;
5518 }
5519 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5520 num_of_elements =
5521 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5522 receiver, true, true, range, visitor);
5523 break;
5524 }
5525 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5526 num_of_elements =
5527 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5528 receiver, true, true, range, visitor);
5529 break;
5530 }
5531 case JSObject::EXTERNAL_INT_ELEMENTS: {
5532 num_of_elements =
5533 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5534 receiver, true, false, range, visitor);
5535 break;
5536 }
5537 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5538 num_of_elements =
5539 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5540 receiver, true, false, range, visitor);
5541 break;
5542 }
5543 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5544 num_of_elements =
5545 IterateExternalArrayElements<ExternalFloatArray, float>(
5546 receiver, false, false, range, visitor);
5547 break;
5548 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005549 case JSObject::DICTIONARY_ELEMENTS: {
5550 Handle<NumberDictionary> dict(receiver->element_dictionary());
5551 uint32_t capacity = dict->Capacity();
5552 for (uint32_t j = 0; j < capacity; j++) {
5553 Handle<Object> k(dict->KeyAt(j));
5554 if (dict->IsKey(*k)) {
5555 ASSERT(k->IsNumber());
5556 uint32_t index = static_cast<uint32_t>(k->Number());
5557 if (index < range) {
5558 num_of_elements++;
5559 if (visitor) {
5560 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5561 }
5562 }
5563 }
5564 }
5565 break;
5566 }
5567 default:
5568 UNREACHABLE();
5569 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005570 }
5571
5572 return num_of_elements;
5573}
5574
5575
5576/**
5577 * A helper function that visits elements of an Array object, and elements
5578 * on its prototypes.
5579 *
5580 * Elements on prototypes are visited first, and only elements whose indices
5581 * less than Array length are visited.
5582 *
5583 * If a ArrayConcatVisitor object is given, the visitor is called with
5584 * parameters, element's index + visitor_index_offset and the element.
5585 */
5586static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5587 ArrayConcatVisitor* visitor) {
5588 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5589 Handle<Object> obj = array;
5590
5591 static const int kEstimatedPrototypes = 3;
5592 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5593
5594 // Visit prototype first. If an element on the prototype is shadowed by
5595 // the inheritor using the same index, the ArrayConcatVisitor visits
5596 // the prototype element before the shadowing element.
5597 // The visitor can simply overwrite the old value by new value using
5598 // the same index. This follows Array::concat semantics.
5599 while (!obj->IsNull()) {
5600 objects.Add(Handle<JSObject>::cast(obj));
5601 obj = Handle<Object>(obj->GetPrototype());
5602 }
5603
5604 uint32_t nof_elements = 0;
5605 for (int i = objects.length() - 1; i >= 0; i--) {
5606 Handle<JSObject> obj = objects[i];
5607 nof_elements +=
5608 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5609 }
5610
5611 return nof_elements;
5612}
5613
5614
5615/**
5616 * A helper function of Runtime_ArrayConcat.
5617 *
5618 * The first argument is an Array of arrays and objects. It is the
5619 * same as the arguments array of Array::concat JS function.
5620 *
5621 * If an argument is an Array object, the function visits array
5622 * elements. If an argument is not an Array object, the function
5623 * visits the object as if it is an one-element array.
5624 *
5625 * If the result array index overflows 32-bit integer, the rounded
5626 * non-negative number is used as new length. For example, if one
5627 * array length is 2^32 - 1, second array length is 1, the
5628 * concatenated array length is 0.
5629 */
5630static uint32_t IterateArguments(Handle<JSArray> arguments,
5631 ArrayConcatVisitor* visitor) {
5632 uint32_t visited_elements = 0;
5633 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5634
5635 for (uint32_t i = 0; i < num_of_args; i++) {
5636 Handle<Object> obj(arguments->GetElement(i));
5637 if (obj->IsJSArray()) {
5638 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5639 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5640 uint32_t nof_elements =
5641 IterateArrayAndPrototypeElements(array, visitor);
5642 // Total elements of array and its prototype chain can be more than
5643 // the array length, but ArrayConcat can only concatenate at most
5644 // the array length number of elements.
5645 visited_elements += (nof_elements > len) ? len : nof_elements;
5646 if (visitor) visitor->increase_index_offset(len);
5647
5648 } else {
5649 if (visitor) {
5650 visitor->visit(0, obj);
5651 visitor->increase_index_offset(1);
5652 }
5653 visited_elements++;
5654 }
5655 }
5656 return visited_elements;
5657}
5658
5659
5660/**
5661 * Array::concat implementation.
5662 * See ECMAScript 262, 15.4.4.4.
5663 */
5664static Object* Runtime_ArrayConcat(Arguments args) {
5665 ASSERT(args.length() == 1);
5666 HandleScope handle_scope;
5667
5668 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5669 Handle<JSArray> arguments(arg_arrays);
5670
5671 // Pass 1: estimate the number of elements of the result
5672 // (it could be more than real numbers if prototype has elements).
5673 uint32_t result_length = 0;
5674 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5675
5676 { AssertNoAllocation nogc;
5677 for (uint32_t i = 0; i < num_of_args; i++) {
5678 Object* obj = arguments->GetElement(i);
5679 if (obj->IsJSArray()) {
5680 result_length +=
5681 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5682 } else {
5683 result_length++;
5684 }
5685 }
5686 }
5687
5688 // Allocate an empty array, will set length and content later.
5689 Handle<JSArray> result = Factory::NewJSArray(0);
5690
5691 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5692 // If estimated number of elements is more than half of length, a
5693 // fixed array (fast case) is more time and space-efficient than a
5694 // dictionary.
5695 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5696
5697 Handle<FixedArray> storage;
5698 if (fast_case) {
5699 // The backing storage array must have non-existing elements to
5700 // preserve holes across concat operations.
5701 storage = Factory::NewFixedArrayWithHoles(result_length);
5702
5703 } else {
5704 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5705 uint32_t at_least_space_for = estimate_nof_elements +
5706 (estimate_nof_elements >> 2);
5707 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005708 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005709 }
5710
5711 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5712
5713 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5714
5715 IterateArguments(arguments, &visitor);
5716
5717 result->set_length(*len);
5718 result->set_elements(*storage);
5719
5720 return *result;
5721}
5722
5723
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005724// This will not allocate (flatten the string), but it may run
5725// very slowly for very deeply nested ConsStrings. For debugging use only.
5726static Object* Runtime_GlobalPrint(Arguments args) {
5727 NoHandleAllocation ha;
5728 ASSERT(args.length() == 1);
5729
5730 CONVERT_CHECKED(String, string, args[0]);
5731 StringInputBuffer buffer(string);
5732 while (buffer.has_more()) {
5733 uint16_t character = buffer.GetNext();
5734 PrintF("%c", character);
5735 }
5736 return string;
5737}
5738
ager@chromium.org5ec48922009-05-05 07:25:34 +00005739// Moves all own elements of an object, that are below a limit, to positions
5740// starting at zero. All undefined values are placed after non-undefined values,
5741// and are followed by non-existing element. Does not change the length
5742// property.
5743// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005744static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005745 ASSERT(args.length() == 2);
5746 CONVERT_CHECKED(JSObject, object, args[0]);
5747 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5748 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005749}
5750
5751
5752// Move contents of argument 0 (an array) to argument 1 (an array)
5753static Object* Runtime_MoveArrayContents(Arguments args) {
5754 ASSERT(args.length() == 2);
5755 CONVERT_CHECKED(JSArray, from, args[0]);
5756 CONVERT_CHECKED(JSArray, to, args[1]);
5757 to->SetContent(FixedArray::cast(from->elements()));
5758 to->set_length(from->length());
5759 from->SetContent(Heap::empty_fixed_array());
5760 from->set_length(0);
5761 return to;
5762}
5763
5764
5765// How many elements does this array have?
5766static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5767 ASSERT(args.length() == 1);
5768 CONVERT_CHECKED(JSArray, array, args[0]);
5769 HeapObject* elements = array->elements();
5770 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005771 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005772 } else {
5773 return array->length();
5774 }
5775}
5776
5777
5778// Returns an array that tells you where in the [0, length) interval an array
5779// might have elements. Can either return keys or intervals. Keys can have
5780// gaps in (undefined). Intervals can also span over some undefined keys.
5781static Object* Runtime_GetArrayKeys(Arguments args) {
5782 ASSERT(args.length() == 2);
5783 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005784 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005785 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005786 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005787 // Create an array and get all the keys into it, then remove all the
5788 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005789 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005790 int keys_length = keys->length();
5791 for (int i = 0; i < keys_length; i++) {
5792 Object* key = keys->get(i);
5793 uint32_t index;
5794 if (!Array::IndexFromObject(key, &index) || index >= length) {
5795 // Zap invalid keys.
5796 keys->set_undefined(i);
5797 }
5798 }
5799 return *Factory::NewJSArrayWithElements(keys);
5800 } else {
5801 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5802 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005803 single_interval->set(0,
5804 Smi::FromInt(-1),
5805 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005806 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5807 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005809 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005810 single_interval->set(1, *length_object);
5811 return *Factory::NewJSArrayWithElements(single_interval);
5812 }
5813}
5814
5815
5816// DefineAccessor takes an optional final argument which is the
5817// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5818// to the way accessors are implemented, it is set for both the getter
5819// and setter on the first call to DefineAccessor and ignored on
5820// subsequent calls.
5821static Object* Runtime_DefineAccessor(Arguments args) {
5822 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5823 // Compute attributes.
5824 PropertyAttributes attributes = NONE;
5825 if (args.length() == 5) {
5826 CONVERT_CHECKED(Smi, attrs, args[4]);
5827 int value = attrs->value();
5828 // Only attribute bits should be set.
5829 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5830 attributes = static_cast<PropertyAttributes>(value);
5831 }
5832
5833 CONVERT_CHECKED(JSObject, obj, args[0]);
5834 CONVERT_CHECKED(String, name, args[1]);
5835 CONVERT_CHECKED(Smi, flag, args[2]);
5836 CONVERT_CHECKED(JSFunction, fun, args[3]);
5837 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5838}
5839
5840
5841static Object* Runtime_LookupAccessor(Arguments args) {
5842 ASSERT(args.length() == 3);
5843 CONVERT_CHECKED(JSObject, obj, args[0]);
5844 CONVERT_CHECKED(String, name, args[1]);
5845 CONVERT_CHECKED(Smi, flag, args[2]);
5846 return obj->LookupAccessor(name, flag->value() == 0);
5847}
5848
5849
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005850#ifdef ENABLE_DEBUGGER_SUPPORT
5851static Object* Runtime_DebugBreak(Arguments args) {
5852 ASSERT(args.length() == 0);
5853 return Execution::DebugBreakHelper();
5854}
5855
5856
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005857// Helper functions for wrapping and unwrapping stack frame ids.
5858static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005859 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005860 return Smi::FromInt(id >> 2);
5861}
5862
5863
5864static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5865 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5866}
5867
5868
5869// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005870// args[0]: debug event listener function to set or null or undefined for
5871// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005872// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005873static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005874 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005875 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5876 args[0]->IsUndefined() ||
5877 args[0]->IsNull());
5878 Handle<Object> callback = args.at<Object>(0);
5879 Handle<Object> data = args.at<Object>(1);
5880 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005881
5882 return Heap::undefined_value();
5883}
5884
5885
5886static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005887 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005888 StackGuard::DebugBreak();
5889 return Heap::undefined_value();
5890}
5891
5892
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005893// Find the length of the prototype chain that is to to handled as one. If a
5894// prototype object is hidden it is to be viewed as part of the the object it
5895// is prototype for.
5896static int LocalPrototypeChainLength(JSObject* obj) {
5897 int count = 1;
5898 Object* proto = obj->GetPrototype();
5899 while (proto->IsJSObject() &&
5900 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5901 count++;
5902 proto = JSObject::cast(proto)->GetPrototype();
5903 }
5904 return count;
5905}
5906
5907
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005908static Object* DebugLookupResultValue(Object* receiver, String* name,
5909 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005910 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005911 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005912 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005913 case NORMAL:
5914 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005915 if (value->IsTheHole()) {
5916 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005917 }
5918 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005919 case FIELD:
5920 value =
5921 JSObject::cast(
5922 result->holder())->FastPropertyAt(result->GetFieldIndex());
5923 if (value->IsTheHole()) {
5924 return Heap::undefined_value();
5925 }
5926 return value;
5927 case CONSTANT_FUNCTION:
5928 return result->GetConstantFunction();
5929 case CALLBACKS: {
5930 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005931 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005932 value = receiver->GetPropertyWithCallback(
5933 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005934 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005935 value = Top::pending_exception();
5936 Top::clear_pending_exception();
5937 if (caught_exception != NULL) {
5938 *caught_exception = true;
5939 }
5940 }
5941 return value;
5942 } else {
5943 return Heap::undefined_value();
5944 }
5945 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005946 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005947 case MAP_TRANSITION:
5948 case CONSTANT_TRANSITION:
5949 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005950 return Heap::undefined_value();
5951 default:
5952 UNREACHABLE();
5953 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005954 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005955 return Heap::undefined_value();
5956}
5957
5958
ager@chromium.org32912102009-01-16 10:38:43 +00005959// Get debugger related details for an object property.
5960// args[0]: object holding property
5961// args[1]: name of the property
5962//
5963// The array returned contains the following information:
5964// 0: Property value
5965// 1: Property details
5966// 2: Property value is exception
5967// 3: Getter function if defined
5968// 4: Setter function if defined
5969// Items 2-4 are only filled if the property has either a getter or a setter
5970// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005971static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005972 HandleScope scope;
5973
5974 ASSERT(args.length() == 2);
5975
5976 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5977 CONVERT_ARG_CHECKED(String, name, 1);
5978
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005979 // Make sure to set the current context to the context before the debugger was
5980 // entered (if the debugger is entered). The reason for switching context here
5981 // is that for some property lookups (accessors and interceptors) callbacks
5982 // into the embedding application can occour, and the embedding application
5983 // could have the assumption that its own global context is the current
5984 // context and not some internal debugger context.
5985 SaveContext save;
5986 if (Debug::InDebugger()) {
5987 Top::set_context(*Debug::debugger_entry()->GetContext());
5988 }
5989
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005990 // Skip the global proxy as it has no properties and always delegates to the
5991 // real global object.
5992 if (obj->IsJSGlobalProxy()) {
5993 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5994 }
5995
5996
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005997 // Check if the name is trivially convertible to an index and get the element
5998 // if so.
5999 uint32_t index;
6000 if (name->AsArrayIndex(&index)) {
6001 Handle<FixedArray> details = Factory::NewFixedArray(2);
6002 details->set(0, Runtime::GetElementOrCharAt(obj, index));
6003 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
6004 return *Factory::NewJSArrayWithElements(details);
6005 }
6006
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006007 // Find the number of objects making up this.
6008 int length = LocalPrototypeChainLength(*obj);
6009
6010 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006011 Handle<JSObject> jsproto = obj;
6012 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006013 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006014 jsproto->LocalLookup(*name, &result);
6015 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006016 // LookupResult is not GC safe as it holds raw object pointers.
6017 // GC can happen later in this code so put the required fields into
6018 // local variables using handles when required for later use.
6019 PropertyType result_type = result.type();
6020 Handle<Object> result_callback_obj;
6021 if (result_type == CALLBACKS) {
6022 result_callback_obj = Handle<Object>(result.GetCallbackObject());
6023 }
6024 Smi* property_details = result.GetPropertyDetails().AsSmi();
6025 // DebugLookupResultValue can cause GC so details from LookupResult needs
6026 // to be copied to handles before this.
6027 bool caught_exception = false;
6028 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
6029 &caught_exception);
6030 if (raw_value->IsFailure()) return raw_value;
6031 Handle<Object> value(raw_value);
6032
6033 // If the callback object is a fixed array then it contains JavaScript
6034 // getter and/or setter.
6035 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
6036 result_callback_obj->IsFixedArray();
6037 Handle<FixedArray> details =
6038 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
6039 details->set(0, *value);
6040 details->set(1, property_details);
6041 if (hasJavaScriptAccessors) {
6042 details->set(2,
6043 caught_exception ? Heap::true_value()
6044 : Heap::false_value());
6045 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
6046 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
6047 }
6048
6049 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006050 }
6051 if (i < length - 1) {
6052 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6053 }
6054 }
6055
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006056 return Heap::undefined_value();
6057}
6058
6059
6060static Object* Runtime_DebugGetProperty(Arguments args) {
6061 HandleScope scope;
6062
6063 ASSERT(args.length() == 2);
6064
6065 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6066 CONVERT_ARG_CHECKED(String, name, 1);
6067
6068 LookupResult result;
6069 obj->Lookup(*name, &result);
6070 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006071 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006072 }
6073 return Heap::undefined_value();
6074}
6075
6076
6077// Return the names of the local named properties.
6078// args[0]: object
6079static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
6080 HandleScope scope;
6081 ASSERT(args.length() == 1);
6082 if (!args[0]->IsJSObject()) {
6083 return Heap::undefined_value();
6084 }
6085 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6086
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006087 // Skip the global proxy as it has no properties and always delegates to the
6088 // real global object.
6089 if (obj->IsJSGlobalProxy()) {
6090 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6091 }
6092
6093 // Find the number of objects making up this.
6094 int length = LocalPrototypeChainLength(*obj);
6095
6096 // Find the number of local properties for each of the objects.
6097 int* local_property_count = NewArray<int>(length);
6098 int total_property_count = 0;
6099 Handle<JSObject> jsproto = obj;
6100 for (int i = 0; i < length; i++) {
6101 int n;
6102 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
6103 local_property_count[i] = n;
6104 total_property_count += n;
6105 if (i < length - 1) {
6106 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6107 }
6108 }
6109
6110 // Allocate an array with storage for all the property names.
6111 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
6112
6113 // Get the property names.
6114 jsproto = obj;
ager@chromium.orgc730f772009-11-11 10:11:16 +00006115 int proto_with_hidden_properties = 0;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006116 for (int i = 0; i < length; i++) {
6117 jsproto->GetLocalPropertyNames(*names,
6118 i == 0 ? 0 : local_property_count[i - 1]);
ager@chromium.orgc730f772009-11-11 10:11:16 +00006119 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
6120 proto_with_hidden_properties++;
6121 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006122 if (i < length - 1) {
6123 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6124 }
6125 }
6126
ager@chromium.orgc730f772009-11-11 10:11:16 +00006127 // Filter out name of hidden propeties object.
6128 if (proto_with_hidden_properties > 0) {
6129 Handle<FixedArray> old_names = names;
6130 names = Factory::NewFixedArray(
6131 names->length() - proto_with_hidden_properties);
6132 int dest_pos = 0;
6133 for (int i = 0; i < total_property_count; i++) {
6134 Object* name = old_names->get(i);
6135 if (name == Heap::hidden_symbol()) {
6136 continue;
6137 }
6138 names->set(dest_pos++, name);
6139 }
6140 }
6141
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006142 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006143 return *Factory::NewJSArrayWithElements(names);
6144}
6145
6146
6147// Return the names of the local indexed properties.
6148// args[0]: object
6149static Object* Runtime_DebugLocalElementNames(Arguments args) {
6150 HandleScope scope;
6151 ASSERT(args.length() == 1);
6152 if (!args[0]->IsJSObject()) {
6153 return Heap::undefined_value();
6154 }
6155 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6156
6157 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
6158 Handle<FixedArray> names = Factory::NewFixedArray(n);
6159 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
6160 return *Factory::NewJSArrayWithElements(names);
6161}
6162
6163
6164// Return the property type calculated from the property details.
6165// args[0]: smi with property details.
6166static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6167 ASSERT(args.length() == 1);
6168 CONVERT_CHECKED(Smi, details, args[0]);
6169 PropertyType type = PropertyDetails(details).type();
6170 return Smi::FromInt(static_cast<int>(type));
6171}
6172
6173
6174// Return the property attribute calculated from the property details.
6175// args[0]: smi with property details.
6176static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6177 ASSERT(args.length() == 1);
6178 CONVERT_CHECKED(Smi, details, args[0]);
6179 PropertyAttributes attributes = PropertyDetails(details).attributes();
6180 return Smi::FromInt(static_cast<int>(attributes));
6181}
6182
6183
6184// Return the property insertion index calculated from the property details.
6185// args[0]: smi with property details.
6186static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6187 ASSERT(args.length() == 1);
6188 CONVERT_CHECKED(Smi, details, args[0]);
6189 int index = PropertyDetails(details).index();
6190 return Smi::FromInt(index);
6191}
6192
6193
6194// Return information on whether an object has a named or indexed interceptor.
6195// args[0]: object
6196static Object* Runtime_DebugInterceptorInfo(Arguments args) {
6197 HandleScope scope;
6198 ASSERT(args.length() == 1);
6199 if (!args[0]->IsJSObject()) {
6200 return Smi::FromInt(0);
6201 }
6202 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6203
6204 int result = 0;
6205 if (obj->HasNamedInterceptor()) result |= 2;
6206 if (obj->HasIndexedInterceptor()) result |= 1;
6207
6208 return Smi::FromInt(result);
6209}
6210
6211
6212// Return property names from named interceptor.
6213// args[0]: object
6214static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
6215 HandleScope scope;
6216 ASSERT(args.length() == 1);
6217 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006218
ager@chromium.org32912102009-01-16 10:38:43 +00006219 if (obj->HasNamedInterceptor()) {
6220 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
6221 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6222 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006223 return Heap::undefined_value();
6224}
6225
6226
6227// Return element names from indexed interceptor.
6228// args[0]: object
6229static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
6230 HandleScope scope;
6231 ASSERT(args.length() == 1);
6232 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006233
ager@chromium.org32912102009-01-16 10:38:43 +00006234 if (obj->HasIndexedInterceptor()) {
6235 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
6236 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6237 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006238 return Heap::undefined_value();
6239}
6240
6241
6242// Return property value from named interceptor.
6243// args[0]: object
6244// args[1]: property name
6245static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6246 HandleScope scope;
6247 ASSERT(args.length() == 2);
6248 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6249 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6250 CONVERT_ARG_CHECKED(String, name, 1);
6251
6252 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006253 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006254}
6255
6256
6257// Return element value from indexed interceptor.
6258// args[0]: object
6259// args[1]: index
6260static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6261 HandleScope scope;
6262 ASSERT(args.length() == 2);
6263 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6264 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6265 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6266
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006267 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006268}
6269
6270
6271static Object* Runtime_CheckExecutionState(Arguments args) {
6272 ASSERT(args.length() >= 1);
6273 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006274 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006275 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006276 return Top::Throw(Heap::illegal_execution_state_symbol());
6277 }
6278
6279 return Heap::true_value();
6280}
6281
6282
6283static Object* Runtime_GetFrameCount(Arguments args) {
6284 HandleScope scope;
6285 ASSERT(args.length() == 1);
6286
6287 // Check arguments.
6288 Object* result = Runtime_CheckExecutionState(args);
6289 if (result->IsFailure()) return result;
6290
6291 // Count all frames which are relevant to debugging stack trace.
6292 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006293 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006294 if (id == StackFrame::NO_ID) {
6295 // If there is no JavaScript stack frame count is 0.
6296 return Smi::FromInt(0);
6297 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006298 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6299 return Smi::FromInt(n);
6300}
6301
6302
6303static const int kFrameDetailsFrameIdIndex = 0;
6304static const int kFrameDetailsReceiverIndex = 1;
6305static const int kFrameDetailsFunctionIndex = 2;
6306static const int kFrameDetailsArgumentCountIndex = 3;
6307static const int kFrameDetailsLocalCountIndex = 4;
6308static const int kFrameDetailsSourcePositionIndex = 5;
6309static const int kFrameDetailsConstructCallIndex = 6;
6310static const int kFrameDetailsDebuggerFrameIndex = 7;
6311static const int kFrameDetailsFirstDynamicIndex = 8;
6312
6313// Return an array with frame details
6314// args[0]: number: break id
6315// args[1]: number: frame index
6316//
6317// The array returned contains the following information:
6318// 0: Frame id
6319// 1: Receiver
6320// 2: Function
6321// 3: Argument count
6322// 4: Local count
6323// 5: Source position
6324// 6: Constructor call
6325// 7: Debugger frame
6326// Arguments name, value
6327// Locals name, value
6328static Object* Runtime_GetFrameDetails(Arguments args) {
6329 HandleScope scope;
6330 ASSERT(args.length() == 2);
6331
6332 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006333 Object* check = Runtime_CheckExecutionState(args);
6334 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006335 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6336
6337 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006338 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006339 if (id == StackFrame::NO_ID) {
6340 // If there are no JavaScript stack frames return undefined.
6341 return Heap::undefined_value();
6342 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006343 int count = 0;
6344 JavaScriptFrameIterator it(id);
6345 for (; !it.done(); it.Advance()) {
6346 if (count == index) break;
6347 count++;
6348 }
6349 if (it.done()) return Heap::undefined_value();
6350
6351 // Traverse the saved contexts chain to find the active context for the
6352 // selected frame.
6353 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006354 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006355 save = save->prev();
6356 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006357 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006358
6359 // Get the frame id.
6360 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6361
6362 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006363 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006364
6365 // Check for constructor frame.
6366 bool constructor = it.frame()->IsConstructor();
6367
6368 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006369 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006370 ScopeInfo<> info(*code);
6371
6372 // Get the context.
6373 Handle<Context> context(Context::cast(it.frame()->context()));
6374
6375 // Get the locals names and values into a temporary array.
6376 //
6377 // TODO(1240907): Hide compiler-introduced stack variables
6378 // (e.g. .result)? For users of the debugger, they will probably be
6379 // confusing.
6380 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6381 for (int i = 0; i < info.NumberOfLocals(); i++) {
6382 // Name of the local.
6383 locals->set(i * 2, *info.LocalName(i));
6384
6385 // Fetch the value of the local - either from the stack or from a
6386 // heap-allocated context.
6387 if (i < info.number_of_stack_slots()) {
6388 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6389 } else {
6390 Handle<String> name = info.LocalName(i);
6391 // Traverse the context chain to the function context as all local
6392 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006393 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006394 context = Handle<Context>(context->previous());
6395 }
6396 ASSERT(context->is_function_context());
6397 locals->set(i * 2 + 1,
6398 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6399 NULL)));
6400 }
6401 }
6402
6403 // Now advance to the arguments adapter frame (if any). If contains all
6404 // the provided parameters and
6405
6406 // Now advance to the arguments adapter frame (if any). It contains all
6407 // the provided parameters whereas the function frame always have the number
6408 // of arguments matching the functions parameters. The rest of the
6409 // information (except for what is collected above) is the same.
6410 it.AdvanceToArgumentsFrame();
6411
6412 // Find the number of arguments to fill. At least fill the number of
6413 // parameters for the function and fill more if more parameters are provided.
6414 int argument_count = info.number_of_parameters();
6415 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6416 argument_count = it.frame()->GetProvidedParametersCount();
6417 }
6418
6419 // Calculate the size of the result.
6420 int details_size = kFrameDetailsFirstDynamicIndex +
6421 2 * (argument_count + info.NumberOfLocals());
6422 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6423
6424 // Add the frame id.
6425 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6426
6427 // Add the function (same as in function frame).
6428 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6429
6430 // Add the arguments count.
6431 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6432
6433 // Add the locals count
6434 details->set(kFrameDetailsLocalCountIndex,
6435 Smi::FromInt(info.NumberOfLocals()));
6436
6437 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006438 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006439 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6440 } else {
6441 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6442 }
6443
6444 // Add the constructor information.
6445 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6446
6447 // Add information on whether this frame is invoked in the debugger context.
6448 details->set(kFrameDetailsDebuggerFrameIndex,
6449 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6450
6451 // Fill the dynamic part.
6452 int details_index = kFrameDetailsFirstDynamicIndex;
6453
6454 // Add arguments name and value.
6455 for (int i = 0; i < argument_count; i++) {
6456 // Name of the argument.
6457 if (i < info.number_of_parameters()) {
6458 details->set(details_index++, *info.parameter_name(i));
6459 } else {
6460 details->set(details_index++, Heap::undefined_value());
6461 }
6462
6463 // Parameter value.
6464 if (i < it.frame()->GetProvidedParametersCount()) {
6465 details->set(details_index++, it.frame()->GetParameter(i));
6466 } else {
6467 details->set(details_index++, Heap::undefined_value());
6468 }
6469 }
6470
6471 // Add locals name and value from the temporary copy from the function frame.
6472 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6473 details->set(details_index++, locals->get(i));
6474 }
6475
6476 // Add the receiver (same as in function frame).
6477 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6478 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6479 Handle<Object> receiver(it.frame()->receiver());
6480 if (!receiver->IsJSObject()) {
6481 // If the receiver is NOT a JSObject we have hit an optimization
6482 // where a value object is not converted into a wrapped JS objects.
6483 // To hide this optimization from the debugger, we wrap the receiver
6484 // by creating correct wrapper object based on the calling frame's
6485 // global context.
6486 it.Advance();
6487 Handle<Context> calling_frames_global_context(
6488 Context::cast(Context::cast(it.frame()->context())->global_context()));
6489 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6490 }
6491 details->set(kFrameDetailsReceiverIndex, *receiver);
6492
6493 ASSERT_EQ(details_size, details_index);
6494 return *Factory::NewJSArrayWithElements(details);
6495}
6496
6497
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006498// Copy all the context locals into an object used to materialize a scope.
6499static void CopyContextLocalsToScopeObject(Handle<Code> code,
6500 ScopeInfo<>& scope_info,
6501 Handle<Context> context,
6502 Handle<JSObject> scope_object) {
6503 // Fill all context locals to the context extension.
6504 for (int i = Context::MIN_CONTEXT_SLOTS;
6505 i < scope_info.number_of_context_slots();
6506 i++) {
6507 int context_index =
6508 ScopeInfo<>::ContextSlotIndex(*code,
6509 *scope_info.context_slot_name(i),
6510 NULL);
6511
6512 // Don't include the arguments shadow (.arguments) context variable.
6513 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6514 SetProperty(scope_object,
6515 scope_info.context_slot_name(i),
6516 Handle<Object>(context->get(context_index)), NONE);
6517 }
6518 }
6519}
6520
6521
6522// Create a plain JSObject which materializes the local scope for the specified
6523// frame.
6524static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6525 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6526 Handle<Code> code(function->code());
6527 ScopeInfo<> scope_info(*code);
6528
6529 // Allocate and initialize a JSObject with all the arguments, stack locals
6530 // heap locals and extension properties of the debugged function.
6531 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6532
6533 // First fill all parameters.
6534 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6535 SetProperty(local_scope,
6536 scope_info.parameter_name(i),
6537 Handle<Object>(frame->GetParameter(i)), NONE);
6538 }
6539
6540 // Second fill all stack locals.
6541 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6542 SetProperty(local_scope,
6543 scope_info.stack_slot_name(i),
6544 Handle<Object>(frame->GetExpression(i)), NONE);
6545 }
6546
6547 // Third fill all context locals.
6548 Handle<Context> frame_context(Context::cast(frame->context()));
6549 Handle<Context> function_context(frame_context->fcontext());
6550 CopyContextLocalsToScopeObject(code, scope_info,
6551 function_context, local_scope);
6552
6553 // Finally copy any properties from the function context extension. This will
6554 // be variables introduced by eval.
6555 if (function_context->closure() == *function) {
6556 if (function_context->has_extension() &&
6557 !function_context->IsGlobalContext()) {
6558 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006559 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006560 for (int i = 0; i < keys->length(); i++) {
6561 // Names of variables introduced by eval are strings.
6562 ASSERT(keys->get(i)->IsString());
6563 Handle<String> key(String::cast(keys->get(i)));
6564 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6565 }
6566 }
6567 }
6568 return local_scope;
6569}
6570
6571
6572// Create a plain JSObject which materializes the closure content for the
6573// context.
6574static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6575 ASSERT(context->is_function_context());
6576
6577 Handle<Code> code(context->closure()->code());
6578 ScopeInfo<> scope_info(*code);
6579
6580 // Allocate and initialize a JSObject with all the content of theis function
6581 // closure.
6582 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6583
6584 // Check whether the arguments shadow object exists.
6585 int arguments_shadow_index =
6586 ScopeInfo<>::ContextSlotIndex(*code,
6587 Heap::arguments_shadow_symbol(),
6588 NULL);
6589 if (arguments_shadow_index >= 0) {
6590 // In this case all the arguments are available in the arguments shadow
6591 // object.
6592 Handle<JSObject> arguments_shadow(
6593 JSObject::cast(context->get(arguments_shadow_index)));
6594 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6595 SetProperty(closure_scope,
6596 scope_info.parameter_name(i),
6597 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6598 }
6599 }
6600
6601 // Fill all context locals to the context extension.
6602 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6603
6604 // Finally copy any properties from the function context extension. This will
6605 // be variables introduced by eval.
6606 if (context->has_extension()) {
6607 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006608 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006609 for (int i = 0; i < keys->length(); i++) {
6610 // Names of variables introduced by eval are strings.
6611 ASSERT(keys->get(i)->IsString());
6612 Handle<String> key(String::cast(keys->get(i)));
6613 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6614 }
6615 }
6616
6617 return closure_scope;
6618}
6619
6620
6621// Iterate over the actual scopes visible from a stack frame. All scopes are
6622// backed by an actual context except the local scope, which is inserted
6623// "artifically" in the context chain.
6624class ScopeIterator {
6625 public:
6626 enum ScopeType {
6627 ScopeTypeGlobal = 0,
6628 ScopeTypeLocal,
6629 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006630 ScopeTypeClosure,
6631 // Every catch block contains an implicit with block (its parameter is
6632 // a JSContextExtensionObject) that extends current scope with a variable
6633 // holding exception object. Such with blocks are treated as scopes of their
6634 // own type.
6635 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006636 };
6637
6638 explicit ScopeIterator(JavaScriptFrame* frame)
6639 : frame_(frame),
6640 function_(JSFunction::cast(frame->function())),
6641 context_(Context::cast(frame->context())),
6642 local_done_(false),
6643 at_local_(false) {
6644
6645 // Check whether the first scope is actually a local scope.
6646 if (context_->IsGlobalContext()) {
6647 // If there is a stack slot for .result then this local scope has been
6648 // created for evaluating top level code and it is not a real local scope.
6649 // Checking for the existence of .result seems fragile, but the scope info
6650 // saved with the code object does not otherwise have that information.
6651 Handle<Code> code(function_->code());
6652 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6653 at_local_ = index < 0;
6654 } else if (context_->is_function_context()) {
6655 at_local_ = true;
6656 }
6657 }
6658
6659 // More scopes?
6660 bool Done() { return context_.is_null(); }
6661
6662 // Move to the next scope.
6663 void Next() {
6664 // If at a local scope mark the local scope as passed.
6665 if (at_local_) {
6666 at_local_ = false;
6667 local_done_ = true;
6668
6669 // If the current context is not associated with the local scope the
6670 // current context is the next real scope, so don't move to the next
6671 // context in this case.
6672 if (context_->closure() != *function_) {
6673 return;
6674 }
6675 }
6676
6677 // The global scope is always the last in the chain.
6678 if (context_->IsGlobalContext()) {
6679 context_ = Handle<Context>();
6680 return;
6681 }
6682
6683 // Move to the next context.
6684 if (context_->is_function_context()) {
6685 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6686 } else {
6687 context_ = Handle<Context>(context_->previous());
6688 }
6689
6690 // If passing the local scope indicate that the current scope is now the
6691 // local scope.
6692 if (!local_done_ &&
6693 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6694 at_local_ = true;
6695 }
6696 }
6697
6698 // Return the type of the current scope.
6699 int Type() {
6700 if (at_local_) {
6701 return ScopeTypeLocal;
6702 }
6703 if (context_->IsGlobalContext()) {
6704 ASSERT(context_->global()->IsGlobalObject());
6705 return ScopeTypeGlobal;
6706 }
6707 if (context_->is_function_context()) {
6708 return ScopeTypeClosure;
6709 }
6710 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006711 // Current scope is either an explicit with statement or a with statement
6712 // implicitely generated for a catch block.
6713 // If the extension object here is a JSContextExtensionObject then
6714 // current with statement is one frome a catch block otherwise it's a
6715 // regular with statement.
6716 if (context_->extension()->IsJSContextExtensionObject()) {
6717 return ScopeTypeCatch;
6718 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006719 return ScopeTypeWith;
6720 }
6721
6722 // Return the JavaScript object with the content of the current scope.
6723 Handle<JSObject> ScopeObject() {
6724 switch (Type()) {
6725 case ScopeIterator::ScopeTypeGlobal:
6726 return Handle<JSObject>(CurrentContext()->global());
6727 break;
6728 case ScopeIterator::ScopeTypeLocal:
6729 // Materialize the content of the local scope into a JSObject.
6730 return MaterializeLocalScope(frame_);
6731 break;
6732 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006733 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006734 // Return the with object.
6735 return Handle<JSObject>(CurrentContext()->extension());
6736 break;
6737 case ScopeIterator::ScopeTypeClosure:
6738 // Materialize the content of the closure scope into a JSObject.
6739 return MaterializeClosure(CurrentContext());
6740 break;
6741 }
6742 UNREACHABLE();
6743 return Handle<JSObject>();
6744 }
6745
6746 // Return the context for this scope. For the local context there might not
6747 // be an actual context.
6748 Handle<Context> CurrentContext() {
6749 if (at_local_ && context_->closure() != *function_) {
6750 return Handle<Context>();
6751 }
6752 return context_;
6753 }
6754
6755#ifdef DEBUG
6756 // Debug print of the content of the current scope.
6757 void DebugPrint() {
6758 switch (Type()) {
6759 case ScopeIterator::ScopeTypeGlobal:
6760 PrintF("Global:\n");
6761 CurrentContext()->Print();
6762 break;
6763
6764 case ScopeIterator::ScopeTypeLocal: {
6765 PrintF("Local:\n");
6766 Handle<Code> code(function_->code());
6767 ScopeInfo<> scope_info(*code);
6768 scope_info.Print();
6769 if (!CurrentContext().is_null()) {
6770 CurrentContext()->Print();
6771 if (CurrentContext()->has_extension()) {
6772 Handle<JSObject> extension =
6773 Handle<JSObject>(CurrentContext()->extension());
6774 if (extension->IsJSContextExtensionObject()) {
6775 extension->Print();
6776 }
6777 }
6778 }
6779 break;
6780 }
6781
6782 case ScopeIterator::ScopeTypeWith: {
6783 PrintF("With:\n");
6784 Handle<JSObject> extension =
6785 Handle<JSObject>(CurrentContext()->extension());
6786 extension->Print();
6787 break;
6788 }
6789
ager@chromium.orga1645e22009-09-09 19:27:10 +00006790 case ScopeIterator::ScopeTypeCatch: {
6791 PrintF("Catch:\n");
6792 Handle<JSObject> extension =
6793 Handle<JSObject>(CurrentContext()->extension());
6794 extension->Print();
6795 break;
6796 }
6797
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006798 case ScopeIterator::ScopeTypeClosure: {
6799 PrintF("Closure:\n");
6800 CurrentContext()->Print();
6801 if (CurrentContext()->has_extension()) {
6802 Handle<JSObject> extension =
6803 Handle<JSObject>(CurrentContext()->extension());
6804 if (extension->IsJSContextExtensionObject()) {
6805 extension->Print();
6806 }
6807 }
6808 break;
6809 }
6810
6811 default:
6812 UNREACHABLE();
6813 }
6814 PrintF("\n");
6815 }
6816#endif
6817
6818 private:
6819 JavaScriptFrame* frame_;
6820 Handle<JSFunction> function_;
6821 Handle<Context> context_;
6822 bool local_done_;
6823 bool at_local_;
6824
6825 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6826};
6827
6828
6829static Object* Runtime_GetScopeCount(Arguments args) {
6830 HandleScope scope;
6831 ASSERT(args.length() == 2);
6832
6833 // Check arguments.
6834 Object* check = Runtime_CheckExecutionState(args);
6835 if (check->IsFailure()) return check;
6836 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6837
6838 // Get the frame where the debugging is performed.
6839 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6840 JavaScriptFrameIterator it(id);
6841 JavaScriptFrame* frame = it.frame();
6842
6843 // Count the visible scopes.
6844 int n = 0;
6845 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6846 n++;
6847 }
6848
6849 return Smi::FromInt(n);
6850}
6851
6852
6853static const int kScopeDetailsTypeIndex = 0;
6854static const int kScopeDetailsObjectIndex = 1;
6855static const int kScopeDetailsSize = 2;
6856
6857// Return an array with scope details
6858// args[0]: number: break id
6859// args[1]: number: frame index
6860// args[2]: number: scope index
6861//
6862// The array returned contains the following information:
6863// 0: Scope type
6864// 1: Scope object
6865static Object* Runtime_GetScopeDetails(Arguments args) {
6866 HandleScope scope;
6867 ASSERT(args.length() == 3);
6868
6869 // Check arguments.
6870 Object* check = Runtime_CheckExecutionState(args);
6871 if (check->IsFailure()) return check;
6872 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6873 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6874
6875 // Get the frame where the debugging is performed.
6876 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6877 JavaScriptFrameIterator frame_it(id);
6878 JavaScriptFrame* frame = frame_it.frame();
6879
6880 // Find the requested scope.
6881 int n = 0;
6882 ScopeIterator it(frame);
6883 for (; !it.Done() && n < index; it.Next()) {
6884 n++;
6885 }
6886 if (it.Done()) {
6887 return Heap::undefined_value();
6888 }
6889
6890 // Calculate the size of the result.
6891 int details_size = kScopeDetailsSize;
6892 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6893
6894 // Fill in scope details.
6895 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6896 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6897
6898 return *Factory::NewJSArrayWithElements(details);
6899}
6900
6901
6902static Object* Runtime_DebugPrintScopes(Arguments args) {
6903 HandleScope scope;
6904 ASSERT(args.length() == 0);
6905
6906#ifdef DEBUG
6907 // Print the scopes for the top frame.
6908 StackFrameLocator locator;
6909 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6910 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6911 it.DebugPrint();
6912 }
6913#endif
6914 return Heap::undefined_value();
6915}
6916
6917
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006918static Object* Runtime_GetCFrames(Arguments args) {
6919 HandleScope scope;
6920 ASSERT(args.length() == 1);
6921 Object* result = Runtime_CheckExecutionState(args);
6922 if (result->IsFailure()) return result;
6923
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006924#if V8_HOST_ARCH_64_BIT
6925 UNIMPLEMENTED();
6926 return Heap::undefined_value();
6927#else
6928
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006929 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006930 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6931 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006932 if (frames_count == OS::kStackWalkError) {
6933 return Heap::undefined_value();
6934 }
6935
6936 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6937 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6938 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6939 for (int i = 0; i < frames_count; i++) {
6940 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6941 frame_value->SetProperty(
6942 *address_str,
6943 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6944 NONE);
6945
6946 // Get the stack walk text for this frame.
6947 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006948 int frame_text_length = StrLength(frames[i].text);
6949 if (frame_text_length > 0) {
6950 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006951 frame_text = Factory::NewStringFromAscii(str);
6952 }
6953
6954 if (!frame_text.is_null()) {
6955 frame_value->SetProperty(*text_str, *frame_text, NONE);
6956 }
6957
6958 frames_array->set(i, *frame_value);
6959 }
6960 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006961#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006962}
6963
6964
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006965static Object* Runtime_GetThreadCount(Arguments args) {
6966 HandleScope scope;
6967 ASSERT(args.length() == 1);
6968
6969 // Check arguments.
6970 Object* result = Runtime_CheckExecutionState(args);
6971 if (result->IsFailure()) return result;
6972
6973 // Count all archived V8 threads.
6974 int n = 0;
6975 for (ThreadState* thread = ThreadState::FirstInUse();
6976 thread != NULL;
6977 thread = thread->Next()) {
6978 n++;
6979 }
6980
6981 // Total number of threads is current thread and archived threads.
6982 return Smi::FromInt(n + 1);
6983}
6984
6985
6986static const int kThreadDetailsCurrentThreadIndex = 0;
6987static const int kThreadDetailsThreadIdIndex = 1;
6988static const int kThreadDetailsSize = 2;
6989
6990// Return an array with thread details
6991// args[0]: number: break id
6992// args[1]: number: thread index
6993//
6994// The array returned contains the following information:
6995// 0: Is current thread?
6996// 1: Thread id
6997static Object* Runtime_GetThreadDetails(Arguments args) {
6998 HandleScope scope;
6999 ASSERT(args.length() == 2);
7000
7001 // Check arguments.
7002 Object* check = Runtime_CheckExecutionState(args);
7003 if (check->IsFailure()) return check;
7004 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
7005
7006 // Allocate array for result.
7007 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
7008
7009 // Thread index 0 is current thread.
7010 if (index == 0) {
7011 // Fill the details.
7012 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
7013 details->set(kThreadDetailsThreadIdIndex,
7014 Smi::FromInt(ThreadManager::CurrentId()));
7015 } else {
7016 // Find the thread with the requested index.
7017 int n = 1;
7018 ThreadState* thread = ThreadState::FirstInUse();
7019 while (index != n && thread != NULL) {
7020 thread = thread->Next();
7021 n++;
7022 }
7023 if (thread == NULL) {
7024 return Heap::undefined_value();
7025 }
7026
7027 // Fill the details.
7028 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
7029 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
7030 }
7031
7032 // Convert to JS array and return.
7033 return *Factory::NewJSArrayWithElements(details);
7034}
7035
7036
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007037static Object* Runtime_GetBreakLocations(Arguments args) {
7038 HandleScope scope;
7039 ASSERT(args.length() == 1);
7040
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007041 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7042 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007043 // Find the number of break points
7044 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
7045 if (break_locations->IsUndefined()) return Heap::undefined_value();
7046 // Return array as JS array
7047 return *Factory::NewJSArrayWithElements(
7048 Handle<FixedArray>::cast(break_locations));
7049}
7050
7051
7052// Set a break point in a function
7053// args[0]: function
7054// args[1]: number: break source position (within the function source)
7055// args[2]: number: break point object
7056static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
7057 HandleScope scope;
7058 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007059 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7060 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007061 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7062 RUNTIME_ASSERT(source_position >= 0);
7063 Handle<Object> break_point_object_arg = args.at<Object>(2);
7064
7065 // Set break point.
7066 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
7067
7068 return Heap::undefined_value();
7069}
7070
7071
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007072Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
7073 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007074 // Iterate the heap looking for SharedFunctionInfo generated from the
7075 // script. The inner most SharedFunctionInfo containing the source position
7076 // for the requested break point is found.
7077 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
7078 // which is found is not compiled it is compiled and the heap is iterated
7079 // again as the compilation might create inner functions from the newly
7080 // compiled function and the actual requested break point might be in one of
7081 // these functions.
7082 bool done = false;
7083 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00007084 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007085 Handle<SharedFunctionInfo> target;
7086 // The current candidate for the last function in script:
7087 Handle<SharedFunctionInfo> last;
7088 while (!done) {
7089 HeapIterator iterator;
7090 while (iterator.has_next()) {
7091 HeapObject* obj = iterator.next();
7092 ASSERT(obj != NULL);
7093 if (obj->IsSharedFunctionInfo()) {
7094 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
7095 if (shared->script() == *script) {
7096 // If the SharedFunctionInfo found has the requested script data and
7097 // contains the source position it is a candidate.
7098 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00007099 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100 start_position = shared->start_position();
7101 }
7102 if (start_position <= position &&
7103 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007104 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007105 // candidate this is the new candidate.
7106 if (target.is_null()) {
7107 target_start_position = start_position;
7108 target = shared;
7109 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007110 if (target_start_position == start_position &&
7111 shared->end_position() == target->end_position()) {
7112 // If a top-level function contain only one function
7113 // declartion the source for the top-level and the function is
7114 // the same. In that case prefer the non top-level function.
7115 if (!shared->is_toplevel()) {
7116 target_start_position = start_position;
7117 target = shared;
7118 }
7119 } else if (target_start_position <= start_position &&
7120 shared->end_position() <= target->end_position()) {
7121 // This containment check includes equality as a function inside
7122 // a top-level function can share either start or end position
7123 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007124 target_start_position = start_position;
7125 target = shared;
7126 }
7127 }
7128 }
7129
7130 // Keep track of the last function in the script.
7131 if (last.is_null() ||
7132 shared->end_position() > last->start_position()) {
7133 last = shared;
7134 }
7135 }
7136 }
7137 }
7138
7139 // Make sure some candidate is selected.
7140 if (target.is_null()) {
7141 if (!last.is_null()) {
7142 // Position after the last function - use last.
7143 target = last;
7144 } else {
7145 // Unable to find function - possibly script without any function.
7146 return Heap::undefined_value();
7147 }
7148 }
7149
7150 // If the candidate found is compiled we are done. NOTE: when lazy
7151 // compilation of inner functions is introduced some additional checking
7152 // needs to be done here to compile inner functions.
7153 done = target->is_compiled();
7154 if (!done) {
7155 // If the candidate is not compiled compile it to reveal any inner
7156 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007157 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007158 }
7159 }
7160
7161 return *target;
7162}
7163
7164
7165// Change the state of a break point in a script. NOTE: Regarding performance
7166// see the NOTE for GetScriptFromScriptData.
7167// args[0]: script to set break point in
7168// args[1]: number: break source position (within the script source)
7169// args[2]: number: break point object
7170static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7171 HandleScope scope;
7172 ASSERT(args.length() == 3);
7173 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7174 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7175 RUNTIME_ASSERT(source_position >= 0);
7176 Handle<Object> break_point_object_arg = args.at<Object>(2);
7177
7178 // Get the script from the script wrapper.
7179 RUNTIME_ASSERT(wrapper->value()->IsScript());
7180 Handle<Script> script(Script::cast(wrapper->value()));
7181
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007182 Object* result = Runtime::FindSharedFunctionInfoInScript(
7183 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007184 if (!result->IsUndefined()) {
7185 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7186 // Find position within function. The script position might be before the
7187 // source position of the first function.
7188 int position;
7189 if (shared->start_position() > source_position) {
7190 position = 0;
7191 } else {
7192 position = source_position - shared->start_position();
7193 }
7194 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7195 }
7196 return Heap::undefined_value();
7197}
7198
7199
7200// Clear a break point
7201// args[0]: number: break point object
7202static Object* Runtime_ClearBreakPoint(Arguments args) {
7203 HandleScope scope;
7204 ASSERT(args.length() == 1);
7205 Handle<Object> break_point_object_arg = args.at<Object>(0);
7206
7207 // Clear break point.
7208 Debug::ClearBreakPoint(break_point_object_arg);
7209
7210 return Heap::undefined_value();
7211}
7212
7213
7214// Change the state of break on exceptions
7215// args[0]: boolean indicating uncaught exceptions
7216// args[1]: boolean indicating on/off
7217static Object* Runtime_ChangeBreakOnException(Arguments args) {
7218 HandleScope scope;
7219 ASSERT(args.length() == 2);
7220 ASSERT(args[0]->IsNumber());
7221 ASSERT(args[1]->IsBoolean());
7222
7223 // Update break point state
7224 ExceptionBreakType type =
7225 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7226 bool enable = args[1]->ToBoolean()->IsTrue();
7227 Debug::ChangeBreakOnException(type, enable);
7228 return Heap::undefined_value();
7229}
7230
7231
7232// Prepare for stepping
7233// args[0]: break id for checking execution state
7234// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007235// args[2]: number of times to perform the step, for step out it is the number
7236// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007237static Object* Runtime_PrepareStep(Arguments args) {
7238 HandleScope scope;
7239 ASSERT(args.length() == 3);
7240 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007241 Object* check = Runtime_CheckExecutionState(args);
7242 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007243 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7244 return Top::Throw(Heap::illegal_argument_symbol());
7245 }
7246
7247 // Get the step action and check validity.
7248 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7249 if (step_action != StepIn &&
7250 step_action != StepNext &&
7251 step_action != StepOut &&
7252 step_action != StepInMin &&
7253 step_action != StepMin) {
7254 return Top::Throw(Heap::illegal_argument_symbol());
7255 }
7256
7257 // Get the number of steps.
7258 int step_count = NumberToInt32(args[2]);
7259 if (step_count < 1) {
7260 return Top::Throw(Heap::illegal_argument_symbol());
7261 }
7262
ager@chromium.orga1645e22009-09-09 19:27:10 +00007263 // Clear all current stepping setup.
7264 Debug::ClearStepping();
7265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007266 // Prepare step.
7267 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7268 return Heap::undefined_value();
7269}
7270
7271
7272// Clear all stepping set by PrepareStep.
7273static Object* Runtime_ClearStepping(Arguments args) {
7274 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007275 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007276 Debug::ClearStepping();
7277 return Heap::undefined_value();
7278}
7279
7280
7281// Creates a copy of the with context chain. The copy of the context chain is
7282// is linked to the function context supplied.
7283static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7284 Handle<Context> function_context) {
7285 // At the bottom of the chain. Return the function context to link to.
7286 if (context_chain->is_function_context()) {
7287 return function_context;
7288 }
7289
7290 // Recursively copy the with contexts.
7291 Handle<Context> previous(context_chain->previous());
7292 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7293 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007294 CopyWithContextChain(function_context, previous),
7295 extension,
7296 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007297}
7298
7299
7300// Helper function to find or create the arguments object for
7301// Runtime_DebugEvaluate.
7302static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7303 Handle<JSFunction> function,
7304 Handle<Code> code,
7305 const ScopeInfo<>* sinfo,
7306 Handle<Context> function_context) {
7307 // Try to find the value of 'arguments' to pass as parameter. If it is not
7308 // found (that is the debugged function does not reference 'arguments' and
7309 // does not support eval) then create an 'arguments' object.
7310 int index;
7311 if (sinfo->number_of_stack_slots() > 0) {
7312 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7313 if (index != -1) {
7314 return Handle<Object>(frame->GetExpression(index));
7315 }
7316 }
7317
7318 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7319 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7320 NULL);
7321 if (index != -1) {
7322 return Handle<Object>(function_context->get(index));
7323 }
7324 }
7325
7326 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007327 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7328 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007329 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007330 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007331 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007332 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007333 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007334 return arguments;
7335}
7336
7337
7338// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007339// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007340// extension part has all the parameters and locals of the function on the
7341// stack frame. A function which calls eval with the code to evaluate is then
7342// compiled in this context and called in this context. As this context
7343// replaces the context of the function on the stack frame a new (empty)
7344// function is created as well to be used as the closure for the context.
7345// This function and the context acts as replacements for the function on the
7346// stack frame presenting the same view of the values of parameters and
7347// local variables as if the piece of JavaScript was evaluated at the point
7348// where the function on the stack frame is currently stopped.
7349static Object* Runtime_DebugEvaluate(Arguments args) {
7350 HandleScope scope;
7351
7352 // Check the execution state and decode arguments frame and source to be
7353 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007354 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007355 Object* check_result = Runtime_CheckExecutionState(args);
7356 if (check_result->IsFailure()) return check_result;
7357 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7358 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007359 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7360
7361 // Handle the processing of break.
7362 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007363
7364 // Get the frame where the debugging is performed.
7365 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7366 JavaScriptFrameIterator it(id);
7367 JavaScriptFrame* frame = it.frame();
7368 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7369 Handle<Code> code(function->code());
7370 ScopeInfo<> sinfo(*code);
7371
7372 // Traverse the saved contexts chain to find the active context for the
7373 // selected frame.
7374 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007375 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007376 save = save->prev();
7377 }
7378 ASSERT(save != NULL);
7379 SaveContext savex;
7380 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007381
7382 // Create the (empty) function replacing the function on the stack frame for
7383 // the purpose of evaluating in the context created below. It is important
7384 // that this function does not describe any parameters and local variables
7385 // in the context. If it does then this will cause problems with the lookup
7386 // in Context::Lookup, where context slots for parameters and local variables
7387 // are looked at before the extension object.
7388 Handle<JSFunction> go_between =
7389 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7390 go_between->set_context(function->context());
7391#ifdef DEBUG
7392 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7393 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7394 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7395#endif
7396
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007397 // Materialize the content of the local scope into a JSObject.
7398 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007399
7400 // Allocate a new context for the debug evaluation and set the extension
7401 // object build.
7402 Handle<Context> context =
7403 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007404 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007405 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007406 Handle<Context> frame_context(Context::cast(frame->context()));
7407 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007408 context = CopyWithContextChain(frame_context, context);
7409
7410 // Wrap the evaluation statement in a new function compiled in the newly
7411 // created context. The function has one parameter which has to be called
7412 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007413 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007414 // function(arguments,__source__) {return eval(__source__);}
7415 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007416 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007417 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007418 Handle<String> function_source =
7419 Factory::NewStringFromAscii(Vector<const char>(source_str,
7420 source_str_length));
7421 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007422 Compiler::CompileEval(function_source,
7423 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007424 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007425 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007426 if (boilerplate.is_null()) return Failure::Exception();
7427 Handle<JSFunction> compiled_function =
7428 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7429
7430 // Invoke the result of the compilation to get the evaluation function.
7431 bool has_pending_exception;
7432 Handle<Object> receiver(frame->receiver());
7433 Handle<Object> evaluation_function =
7434 Execution::Call(compiled_function, receiver, 0, NULL,
7435 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007436 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007437
7438 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7439 function_context);
7440
7441 // Invoke the evaluation function and return the result.
7442 const int argc = 2;
7443 Object** argv[argc] = { arguments.location(),
7444 Handle<Object>::cast(source).location() };
7445 Handle<Object> result =
7446 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7447 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007448 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007449
7450 // Skip the global proxy as it has no properties and always delegates to the
7451 // real global object.
7452 if (result->IsJSGlobalProxy()) {
7453 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7454 }
7455
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007456 return *result;
7457}
7458
7459
7460static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7461 HandleScope scope;
7462
7463 // Check the execution state and decode arguments frame and source to be
7464 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007465 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007466 Object* check_result = Runtime_CheckExecutionState(args);
7467 if (check_result->IsFailure()) return check_result;
7468 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007469 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7470
7471 // Handle the processing of break.
7472 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007473
7474 // Enter the top context from before the debugger was invoked.
7475 SaveContext save;
7476 SaveContext* top = &save;
7477 while (top != NULL && *top->context() == *Debug::debug_context()) {
7478 top = top->prev();
7479 }
7480 if (top != NULL) {
7481 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007482 }
7483
7484 // Get the global context now set to the top context from before the
7485 // debugger was invoked.
7486 Handle<Context> context = Top::global_context();
7487
7488 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007489 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007490 Handle<JSFunction>(Compiler::CompileEval(source,
7491 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007492 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007493 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007494 if (boilerplate.is_null()) return Failure::Exception();
7495 Handle<JSFunction> compiled_function =
7496 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7497 context));
7498
7499 // Invoke the result of the compilation to get the evaluation function.
7500 bool has_pending_exception;
7501 Handle<Object> receiver = Top::global();
7502 Handle<Object> result =
7503 Execution::Call(compiled_function, receiver, 0, NULL,
7504 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007505 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007506 return *result;
7507}
7508
7509
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007510static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7511 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007512 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007513
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007514 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007515 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007516
7517 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007518 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007519 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7520 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7521 // because using
7522 // instances->set(i, *GetScriptWrapper(script))
7523 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7524 // already have deferenced the instances handle.
7525 Handle<JSValue> wrapper = GetScriptWrapper(script);
7526 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007527 }
7528
7529 // Return result as a JS array.
7530 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7531 Handle<JSArray>::cast(result)->SetContent(*instances);
7532 return *result;
7533}
7534
7535
7536// Helper function used by Runtime_DebugReferencedBy below.
7537static int DebugReferencedBy(JSObject* target,
7538 Object* instance_filter, int max_references,
7539 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007540 JSFunction* arguments_function) {
7541 NoHandleAllocation ha;
7542 AssertNoAllocation no_alloc;
7543
7544 // Iterate the heap.
7545 int count = 0;
7546 JSObject* last = NULL;
7547 HeapIterator iterator;
7548 while (iterator.has_next() &&
7549 (max_references == 0 || count < max_references)) {
7550 // Only look at all JSObjects.
7551 HeapObject* heap_obj = iterator.next();
7552 if (heap_obj->IsJSObject()) {
7553 // Skip context extension objects and argument arrays as these are
7554 // checked in the context of functions using them.
7555 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007556 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007557 obj->map()->constructor() == arguments_function) {
7558 continue;
7559 }
7560
7561 // Check if the JS object has a reference to the object looked for.
7562 if (obj->ReferencesObject(target)) {
7563 // Check instance filter if supplied. This is normally used to avoid
7564 // references from mirror objects (see Runtime_IsInPrototypeChain).
7565 if (!instance_filter->IsUndefined()) {
7566 Object* V = obj;
7567 while (true) {
7568 Object* prototype = V->GetPrototype();
7569 if (prototype->IsNull()) {
7570 break;
7571 }
7572 if (instance_filter == prototype) {
7573 obj = NULL; // Don't add this object.
7574 break;
7575 }
7576 V = prototype;
7577 }
7578 }
7579
7580 if (obj != NULL) {
7581 // Valid reference found add to instance array if supplied an update
7582 // count.
7583 if (instances != NULL && count < instances_size) {
7584 instances->set(count, obj);
7585 }
7586 last = obj;
7587 count++;
7588 }
7589 }
7590 }
7591 }
7592
7593 // Check for circular reference only. This can happen when the object is only
7594 // referenced from mirrors and has a circular reference in which case the
7595 // object is not really alive and would have been garbage collected if not
7596 // referenced from the mirror.
7597 if (count == 1 && last == target) {
7598 count = 0;
7599 }
7600
7601 // Return the number of referencing objects found.
7602 return count;
7603}
7604
7605
7606// Scan the heap for objects with direct references to an object
7607// args[0]: the object to find references to
7608// args[1]: constructor function for instances to exclude (Mirror)
7609// args[2]: the the maximum number of objects to return
7610static Object* Runtime_DebugReferencedBy(Arguments args) {
7611 ASSERT(args.length() == 3);
7612
7613 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007614 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007615
7616 // Check parameters.
7617 CONVERT_CHECKED(JSObject, target, args[0]);
7618 Object* instance_filter = args[1];
7619 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7620 instance_filter->IsJSObject());
7621 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7622 RUNTIME_ASSERT(max_references >= 0);
7623
7624 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007625 JSObject* arguments_boilerplate =
7626 Top::context()->global_context()->arguments_boilerplate();
7627 JSFunction* arguments_function =
7628 JSFunction::cast(arguments_boilerplate->map()->constructor());
7629
7630 // Get the number of referencing objects.
7631 int count;
7632 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007633 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007634
7635 // Allocate an array to hold the result.
7636 Object* object = Heap::AllocateFixedArray(count);
7637 if (object->IsFailure()) return object;
7638 FixedArray* instances = FixedArray::cast(object);
7639
7640 // Fill the referencing objects.
7641 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007642 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007643
7644 // Return result as JS array.
7645 Object* result =
7646 Heap::AllocateJSObject(
7647 Top::context()->global_context()->array_function());
7648 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7649 return result;
7650}
7651
7652
7653// Helper function used by Runtime_DebugConstructedBy below.
7654static int DebugConstructedBy(JSFunction* constructor, int max_references,
7655 FixedArray* instances, int instances_size) {
7656 AssertNoAllocation no_alloc;
7657
7658 // Iterate the heap.
7659 int count = 0;
7660 HeapIterator iterator;
7661 while (iterator.has_next() &&
7662 (max_references == 0 || count < max_references)) {
7663 // Only look at all JSObjects.
7664 HeapObject* heap_obj = iterator.next();
7665 if (heap_obj->IsJSObject()) {
7666 JSObject* obj = JSObject::cast(heap_obj);
7667 if (obj->map()->constructor() == constructor) {
7668 // Valid reference found add to instance array if supplied an update
7669 // count.
7670 if (instances != NULL && count < instances_size) {
7671 instances->set(count, obj);
7672 }
7673 count++;
7674 }
7675 }
7676 }
7677
7678 // Return the number of referencing objects found.
7679 return count;
7680}
7681
7682
7683// Scan the heap for objects constructed by a specific function.
7684// args[0]: the constructor to find instances of
7685// args[1]: the the maximum number of objects to return
7686static Object* Runtime_DebugConstructedBy(Arguments args) {
7687 ASSERT(args.length() == 2);
7688
7689 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007690 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007691
7692 // Check parameters.
7693 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7694 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7695 RUNTIME_ASSERT(max_references >= 0);
7696
7697 // Get the number of referencing objects.
7698 int count;
7699 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7700
7701 // Allocate an array to hold the result.
7702 Object* object = Heap::AllocateFixedArray(count);
7703 if (object->IsFailure()) return object;
7704 FixedArray* instances = FixedArray::cast(object);
7705
7706 // Fill the referencing objects.
7707 count = DebugConstructedBy(constructor, max_references, instances, count);
7708
7709 // Return result as JS array.
7710 Object* result =
7711 Heap::AllocateJSObject(
7712 Top::context()->global_context()->array_function());
7713 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7714 return result;
7715}
7716
7717
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007718// Find the effective prototype object as returned by __proto__.
7719// args[0]: the object to find the prototype for.
7720static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007721 ASSERT(args.length() == 1);
7722
7723 CONVERT_CHECKED(JSObject, obj, args[0]);
7724
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007725 // Use the __proto__ accessor.
7726 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007727}
7728
7729
7730static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007731 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007732 CPU::DebugBreak();
7733 return Heap::undefined_value();
7734}
7735
7736
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007737static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007738#ifdef DEBUG
7739 HandleScope scope;
7740 ASSERT(args.length() == 1);
7741 // Get the function and make sure it is compiled.
7742 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7743 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7744 return Failure::Exception();
7745 }
7746 func->code()->PrintLn();
7747#endif // DEBUG
7748 return Heap::undefined_value();
7749}
ager@chromium.org9085a012009-05-11 19:22:57 +00007750
7751
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007752static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7753#ifdef DEBUG
7754 HandleScope scope;
7755 ASSERT(args.length() == 1);
7756 // Get the function and make sure it is compiled.
7757 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7758 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7759 return Failure::Exception();
7760 }
7761 func->shared()->construct_stub()->PrintLn();
7762#endif // DEBUG
7763 return Heap::undefined_value();
7764}
7765
7766
ager@chromium.org9085a012009-05-11 19:22:57 +00007767static Object* Runtime_FunctionGetInferredName(Arguments args) {
7768 NoHandleAllocation ha;
7769 ASSERT(args.length() == 1);
7770
7771 CONVERT_CHECKED(JSFunction, f, args[0]);
7772 return f->shared()->inferred_name();
7773}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007774
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007775#endif // ENABLE_DEBUGGER_SUPPORT
7776
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007777#ifdef ENABLE_LOGGING_AND_PROFILING
7778
7779static Object* Runtime_ProfilerResume(Arguments args) {
7780 NoHandleAllocation ha;
7781 ASSERT(args.length() == 1);
7782
7783 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7784 v8::V8::ResumeProfilerEx(smi_modules->value());
7785 return Heap::undefined_value();
7786}
7787
7788
7789static Object* Runtime_ProfilerPause(Arguments args) {
7790 NoHandleAllocation ha;
7791 ASSERT(args.length() == 1);
7792
7793 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7794 v8::V8::PauseProfilerEx(smi_modules->value());
7795 return Heap::undefined_value();
7796}
7797
7798#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007799
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007800// Finds the script object from the script data. NOTE: This operation uses
7801// heap traversal to find the function generated for the source position
7802// for the requested break point. For lazily compiled functions several heap
7803// traversals might be required rendering this operation as a rather slow
7804// operation. However for setting break points which is normally done through
7805// some kind of user interaction the performance is not crucial.
7806static Handle<Object> Runtime_GetScriptFromScriptName(
7807 Handle<String> script_name) {
7808 // Scan the heap for Script objects to find the script with the requested
7809 // script data.
7810 Handle<Script> script;
7811 HeapIterator iterator;
7812 while (script.is_null() && iterator.has_next()) {
7813 HeapObject* obj = iterator.next();
7814 // If a script is found check if it has the script data requested.
7815 if (obj->IsScript()) {
7816 if (Script::cast(obj)->name()->IsString()) {
7817 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7818 script = Handle<Script>(Script::cast(obj));
7819 }
7820 }
7821 }
7822 }
7823
7824 // If no script with the requested script data is found return undefined.
7825 if (script.is_null()) return Factory::undefined_value();
7826
7827 // Return the script found.
7828 return GetScriptWrapper(script);
7829}
7830
7831
7832// Get the script object from script data. NOTE: Regarding performance
7833// see the NOTE for GetScriptFromScriptData.
7834// args[0]: script data for the script to find the source for
7835static Object* Runtime_GetScript(Arguments args) {
7836 HandleScope scope;
7837
7838 ASSERT(args.length() == 1);
7839
7840 CONVERT_CHECKED(String, script_name, args[0]);
7841
7842 // Find the requested script.
7843 Handle<Object> result =
7844 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7845 return *result;
7846}
7847
7848
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007849// Determines whether the given stack frame should be displayed in
7850// a stack trace. The caller is the error constructor that asked
7851// for the stack trace to be collected. The first time a construct
7852// call to this function is encountered it is skipped. The seen_caller
7853// in/out parameter is used to remember if the caller has been seen
7854// yet.
7855static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7856 bool* seen_caller) {
7857 // Only display JS frames.
7858 if (!raw_frame->is_java_script())
7859 return false;
7860 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7861 Object* raw_fun = frame->function();
7862 // Not sure when this can happen but skip it just in case.
7863 if (!raw_fun->IsJSFunction())
7864 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007865 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007866 *seen_caller = true;
7867 return false;
7868 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007869 // Skip all frames until we've seen the caller. Also, skip the most
7870 // obvious builtin calls. Some builtin calls (such as Number.ADD
7871 // which is invoked using 'call') are very difficult to recognize
7872 // so we're leaving them in for now.
7873 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007874}
7875
7876
7877// Collect the raw data for a stack trace. Returns an array of three
7878// element segments each containing a receiver, function and native
7879// code offset.
7880static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007881 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007882 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007883 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7884
7885 HandleScope scope;
7886
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007887 limit = Max(limit, 0); // Ensure that limit is not negative.
7888 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007889 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007890
7891 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007892 // If the caller parameter is a function we skip frames until we're
7893 // under it before starting to collect.
7894 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007895 int cursor = 0;
7896 int frames_seen = 0;
7897 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007898 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007899 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007900 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007901 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007902 Object* recv = frame->receiver();
7903 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007904 Address pc = frame->pc();
7905 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007906 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007907 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007908 if (cursor + 2 < elements->length()) {
7909 elements->set(cursor++, recv);
7910 elements->set(cursor++, fun);
7911 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7912 } else {
7913 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007914 Handle<Object> recv_handle(recv);
7915 Handle<Object> fun_handle(fun);
7916 SetElement(result, cursor++, recv_handle);
7917 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007918 SetElement(result, cursor++, Handle<Smi>(offset));
7919 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007920 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007921 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007922 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007923
7924 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7925
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007926 return *result;
7927}
7928
7929
ager@chromium.org3811b432009-10-28 14:53:37 +00007930// Returns V8 version as a string.
7931static Object* Runtime_GetV8Version(Arguments args) {
7932 ASSERT_EQ(args.length(), 0);
7933
7934 NoHandleAllocation ha;
7935
7936 const char* version_string = v8::V8::GetVersion();
7937
7938 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
7939}
7940
7941
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007942static Object* Runtime_Abort(Arguments args) {
7943 ASSERT(args.length() == 2);
7944 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7945 Smi::cast(args[1])->value());
7946 Top::PrintStack();
7947 OS::Abort();
7948 UNREACHABLE();
7949 return NULL;
7950}
7951
7952
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007953static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
7954 ASSERT(args.length() == 0);
7955 HandleScope::DeleteExtensions();
7956 return Heap::undefined_value();
7957}
7958
7959
kasper.lund44510672008-07-25 07:37:58 +00007960#ifdef DEBUG
7961// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7962// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007963static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007964 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007965 HandleScope scope;
7966 Handle<JSArray> result = Factory::NewJSArray(0);
7967 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007968#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007969 { \
7970 HandleScope inner; \
7971 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007972 Factory::NewStringFromAscii( \
7973 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007974 Handle<JSArray> pair = Factory::NewJSArray(0); \
7975 SetElement(pair, 0, name); \
7976 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7977 SetElement(result, index++, pair); \
7978 }
7979 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7980#undef ADD_ENTRY
7981 return *result;
7982}
kasper.lund44510672008-07-25 07:37:58 +00007983#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007984
7985
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007986static Object* Runtime_Log(Arguments args) {
7987 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007988 CONVERT_CHECKED(String, format, args[0]);
7989 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007990 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007991 Logger::LogRuntime(chars, elms);
7992 return Heap::undefined_value();
7993}
7994
7995
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007996static Object* Runtime_IS_VAR(Arguments args) {
7997 UNREACHABLE(); // implemented as macro in the parser
7998 return NULL;
7999}
8000
8001
8002// ----------------------------------------------------------------------------
8003// Implementation of Runtime
8004
ager@chromium.orga1645e22009-09-09 19:27:10 +00008005#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008006 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00008007 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008008
8009static Runtime::Function Runtime_functions[] = {
8010 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00008011 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008012};
8013
8014#undef F
8015
8016
8017Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
8018 ASSERT(0 <= fid && fid < kNofFunctions);
8019 return &Runtime_functions[fid];
8020}
8021
8022
8023Runtime::Function* Runtime::FunctionForName(const char* name) {
8024 for (Function* f = Runtime_functions; f->name != NULL; f++) {
8025 if (strcmp(f->name, name) == 0) {
8026 return f;
8027 }
8028 }
8029 return NULL;
8030}
8031
8032
8033void Runtime::PerformGC(Object* result) {
8034 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008035 if (failure->IsRetryAfterGC()) {
8036 // Try to do a garbage collection; ignore it if it fails. The C
8037 // entry stub will throw an out-of-memory exception in that case.
8038 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
8039 } else {
8040 // Handle last resort GC and make sure to allow future allocations
8041 // to grow the heap without causing GCs (if possible).
8042 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008043 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008044 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045}
8046
8047
8048} } // namespace v8::internal