blob: 6d3a158068f556761c7a0a9db0a51366a4867526 [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"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000041#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000042#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043#include "platform.h"
44#include "runtime.h"
45#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000046#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000047#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000048#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000049
kasperl@chromium.org71affb52009-05-26 05:44:31 +000050namespace v8 {
51namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000052
53
ager@chromium.org3e875802009-06-29 08:26:34 +000054#define RUNTIME_ASSERT(value) \
55 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000056
57// Cast the given object to a value of the specified type and store
58// it in a variable with the given name. If the object is not of the
59// expected type call IllegalOperation and return.
60#define CONVERT_CHECKED(Type, name, obj) \
61 RUNTIME_ASSERT(obj->Is##Type()); \
62 Type* name = Type::cast(obj);
63
64#define CONVERT_ARG_CHECKED(Type, name, index) \
65 RUNTIME_ASSERT(args[index]->Is##Type()); \
66 Handle<Type> name = args.at<Type>(index);
67
kasper.lundbd3ec4e2008-07-09 11:06:54 +000068// Cast the given object to a boolean and store it in a variable with
69// the given name. If the object is not a boolean call IllegalOperation
70// and return.
71#define CONVERT_BOOLEAN_CHECKED(name, obj) \
72 RUNTIME_ASSERT(obj->IsBoolean()); \
73 bool name = (obj)->IsTrue();
74
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000075// Cast the given object to a Smi and store its value in an int variable
76// with the given name. If the object is not a Smi call IllegalOperation
77// and return.
78#define CONVERT_SMI_CHECKED(name, obj) \
79 RUNTIME_ASSERT(obj->IsSmi()); \
80 int name = Smi::cast(obj)->value();
81
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000082// Cast the given object to a double and store it in a variable with
83// the given name. If the object is not a number (as opposed to
84// the number not-a-number) call IllegalOperation and return.
85#define CONVERT_DOUBLE_CHECKED(name, obj) \
86 RUNTIME_ASSERT(obj->IsNumber()); \
87 double name = (obj)->Number();
88
89// Call the specified converter on the object *comand store the result in
90// a variable of the specified type with the given name. If the
91// object is not a Number call IllegalOperation and return.
92#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
93 RUNTIME_ASSERT(obj->IsNumber()); \
94 type name = NumberTo##Type(obj);
95
96// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000097static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098
99
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000100static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
101 StackLimitCheck check;
102 if (check.HasOverflowed()) return Top::StackOverflow();
103
104 Object* result = Heap::CopyJSObject(boilerplate);
105 if (result->IsFailure()) return result;
106 JSObject* copy = JSObject::cast(result);
107
108 // Deep copy local properties.
109 if (copy->HasFastProperties()) {
110 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000111 for (int i = 0; i < properties->length(); i++) {
112 Object* value = properties->get(i);
113 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000114 JSObject* js_object = JSObject::cast(value);
115 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000116 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000117 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000118 }
119 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000120 int nof = copy->map()->inobject_properties();
121 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000122 Object* value = copy->InObjectPropertyAt(i);
123 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000124 JSObject* js_object = JSObject::cast(value);
125 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000126 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000127 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000128 }
129 }
130 } else {
131 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
132 if (result->IsFailure()) return result;
133 FixedArray* names = FixedArray::cast(result);
134 copy->GetLocalPropertyNames(names, 0);
135 for (int i = 0; i < names->length(); i++) {
136 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000137 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000138 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000139 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000140 // Only deep copy fields from the object literal expression.
141 // In particular, don't try to copy the length attribute of
142 // an array.
143 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000144 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000145 ASSERT(!value->IsFailure());
146 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000147 JSObject* js_object = JSObject::cast(value);
148 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000149 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000150 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000151 if (result->IsFailure()) return result;
152 }
153 }
154 }
155
156 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000157 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000158 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000159 switch (copy->GetElementsKind()) {
160 case JSObject::FAST_ELEMENTS: {
161 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000162 for (int i = 0; i < elements->length(); i++) {
163 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000164 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000165 JSObject* js_object = JSObject::cast(value);
166 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000167 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000168 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000169 }
170 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000171 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000172 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000173 case JSObject::DICTIONARY_ELEMENTS: {
174 NumberDictionary* element_dictionary = copy->element_dictionary();
175 int capacity = element_dictionary->Capacity();
176 for (int i = 0; i < capacity; i++) {
177 Object* k = element_dictionary->KeyAt(i);
178 if (element_dictionary->IsKey(k)) {
179 Object* value = element_dictionary->ValueAt(i);
180 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000181 JSObject* js_object = JSObject::cast(value);
182 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000183 if (result->IsFailure()) return result;
184 element_dictionary->ValueAtPut(i, result);
185 }
186 }
187 }
188 break;
189 }
190 default:
191 UNREACHABLE();
192 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000193 }
194 return copy;
195}
196
197
198static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
199 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
200 return DeepCopyBoilerplate(boilerplate);
201}
202
203
204static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000205 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000206 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000207}
208
209
ager@chromium.org236ad962008-09-25 09:45:57 +0000210static Handle<Map> ComputeObjectLiteralMap(
211 Handle<Context> context,
212 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000213 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000214 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000215 if (FLAG_canonicalize_object_literal_maps) {
216 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000217 int number_of_symbol_keys = 0;
218 while ((number_of_symbol_keys < number_of_properties) &&
219 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
220 number_of_symbol_keys++;
221 }
222 // Based on the number of prefix symbols key we decide whether
223 // to use the map cache in the global context.
224 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000225 if ((number_of_symbol_keys == number_of_properties) &&
226 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000227 // Create the fixed array with the key.
228 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
229 for (int i = 0; i < number_of_symbol_keys; i++) {
230 keys->set(i, constant_properties->get(i*2));
231 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000232 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000233 return Factory::ObjectLiteralMapFromCache(context, keys);
234 }
235 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000236 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000237 return Factory::CopyMap(
238 Handle<Map>(context->object_function()->initial_map()),
239 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000240}
241
242
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000243static Handle<Object> CreateLiteralBoilerplate(
244 Handle<FixedArray> literals,
245 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000246
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000247
248static Handle<Object> CreateObjectLiteralBoilerplate(
249 Handle<FixedArray> literals,
250 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000251 // Get the global context from the literals array. This is the
252 // context in which the function was created and we use the object
253 // function from this context to create the object literal. We do
254 // not use the object function from the current global context
255 // because this might be the object function from another context
256 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000257 Handle<Context> context =
258 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
259
260 bool is_result_from_cache;
261 Handle<Map> map = ComputeObjectLiteralMap(context,
262 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000263 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000264
ager@chromium.org236ad962008-09-25 09:45:57 +0000265 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000266 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000267 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000268 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000269 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000270 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000271 for (int index = 0; index < length; index +=2) {
272 Handle<Object> key(constant_properties->get(index+0));
273 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000274 if (value->IsFixedArray()) {
275 // The value contains the constant_properties of a
276 // simple object literal.
277 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
278 value = CreateLiteralBoilerplate(literals, array);
279 if (value.is_null()) return value;
280 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000281 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000282 uint32_t element_index = 0;
283 if (key->IsSymbol()) {
284 // If key is a symbol it is not an array element.
285 Handle<String> name(String::cast(*key));
286 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 } else if (Array::IndexFromObject(*key, &element_index)) {
289 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000290 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 } else {
292 // Non-uint32 number.
293 ASSERT(key->IsNumber());
294 double num = key->Number();
295 char arr[100];
296 Vector<char> buffer(arr, ARRAY_SIZE(arr));
297 const char* str = DoubleToCString(num, buffer);
298 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000299 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000300 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000301 // If setting the property on the boilerplate throws an
302 // exception, the exception is converted to an empty handle in
303 // the handle based operations. In that case, we need to
304 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000305 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
307 }
308
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000309 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000310}
311
312
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000313static Handle<Object> CreateArrayLiteralBoilerplate(
314 Handle<FixedArray> literals,
315 Handle<FixedArray> elements) {
316 // Create the JSArray.
317 Handle<JSFunction> constructor(
318 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
319 Handle<Object> object = Factory::NewJSObject(constructor);
320
321 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
322
323 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
324 for (int i = 0; i < content->length(); i++) {
325 if (content->get(i)->IsFixedArray()) {
326 // The value contains the constant_properties of a
327 // simple object literal.
328 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
329 Handle<Object> result =
330 CreateLiteralBoilerplate(literals, fa);
331 if (result.is_null()) return result;
332 content->set(i, *result);
333 }
334 }
335
336 // Set the elements.
337 Handle<JSArray>::cast(object)->SetContent(*content);
338 return object;
339}
340
341
342static Handle<Object> CreateLiteralBoilerplate(
343 Handle<FixedArray> literals,
344 Handle<FixedArray> array) {
345 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
346 switch (CompileTimeValue::GetType(array)) {
347 case CompileTimeValue::OBJECT_LITERAL:
348 return CreateObjectLiteralBoilerplate(literals, elements);
349 case CompileTimeValue::ARRAY_LITERAL:
350 return CreateArrayLiteralBoilerplate(literals, elements);
351 default:
352 UNREACHABLE();
353 return Handle<Object>::null();
354 }
355}
356
357
358static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
359 HandleScope scope;
360 ASSERT(args.length() == 3);
361 // Copy the arguments.
362 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
363 CONVERT_SMI_CHECKED(literals_index, args[1]);
364 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
365
366 Handle<Object> result =
367 CreateObjectLiteralBoilerplate(literals, constant_properties);
368
369 if (result.is_null()) return Failure::Exception();
370
371 // Update the functions literal and return the boilerplate.
372 literals->set(literals_index, *result);
373
374 return *result;
375}
376
377
378static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000379 // Takes a FixedArray of elements containing the literal elements of
380 // the array literal and produces JSArray with those elements.
381 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000382 // which contains the context from which to get the Array function
383 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000384 HandleScope scope;
385 ASSERT(args.length() == 3);
386 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
387 CONVERT_SMI_CHECKED(literals_index, args[1]);
388 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000389
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000390 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
391 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000392
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000393 // Update the functions literal and return the boilerplate.
394 literals->set(literals_index, *object);
395 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396}
397
398
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000399static Object* Runtime_CreateObjectLiteral(Arguments args) {
400 HandleScope scope;
401 ASSERT(args.length() == 3);
402 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
403 CONVERT_SMI_CHECKED(literals_index, args[1]);
404 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
405
406 // Check if boilerplate exists. If not, create it first.
407 Handle<Object> boilerplate(literals->get(literals_index));
408 if (*boilerplate == Heap::undefined_value()) {
409 boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
410 if (boilerplate.is_null()) return Failure::Exception();
411 // Update the functions literal and return the boilerplate.
412 literals->set(literals_index, *boilerplate);
413 }
414 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
415}
416
417
418static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
419 HandleScope scope;
420 ASSERT(args.length() == 3);
421 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
422 CONVERT_SMI_CHECKED(literals_index, args[1]);
423 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
424
425 // Check if boilerplate exists. If not, create it first.
426 Handle<Object> boilerplate(literals->get(literals_index));
427 if (*boilerplate == Heap::undefined_value()) {
428 boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
429 if (boilerplate.is_null()) return Failure::Exception();
430 // Update the functions literal and return the boilerplate.
431 literals->set(literals_index, *boilerplate);
432 }
433 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
434}
435
436
437static Object* Runtime_CreateArrayLiteral(Arguments args) {
438 HandleScope scope;
439 ASSERT(args.length() == 3);
440 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
441 CONVERT_SMI_CHECKED(literals_index, args[1]);
442 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
443
444 // Check if boilerplate exists. If not, create it first.
445 Handle<Object> boilerplate(literals->get(literals_index));
446 if (*boilerplate == Heap::undefined_value()) {
447 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
448 if (boilerplate.is_null()) return Failure::Exception();
449 // Update the functions literal and return the boilerplate.
450 literals->set(literals_index, *boilerplate);
451 }
452 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
453}
454
455
456static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
457 HandleScope scope;
458 ASSERT(args.length() == 3);
459 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
460 CONVERT_SMI_CHECKED(literals_index, args[1]);
461 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
462
463 // Check if boilerplate exists. If not, create it first.
464 Handle<Object> boilerplate(literals->get(literals_index));
465 if (*boilerplate == Heap::undefined_value()) {
466 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
467 if (boilerplate.is_null()) return Failure::Exception();
468 // Update the functions literal and return the boilerplate.
469 literals->set(literals_index, *boilerplate);
470 }
471 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
472}
473
474
ager@chromium.org32912102009-01-16 10:38:43 +0000475static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
476 ASSERT(args.length() == 2);
477 CONVERT_CHECKED(String, key, args[0]);
478 Object* value = args[1];
479 // Create a catch context extension object.
480 JSFunction* constructor =
481 Top::context()->global_context()->context_extension_function();
482 Object* object = Heap::AllocateJSObject(constructor);
483 if (object->IsFailure()) return object;
484 // Assign the exception value to the catch variable and make sure
485 // that the catch variable is DontDelete.
486 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
487 if (value->IsFailure()) return value;
488 return object;
489}
490
491
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000492static Object* Runtime_ClassOf(Arguments args) {
493 NoHandleAllocation ha;
494 ASSERT(args.length() == 1);
495 Object* obj = args[0];
496 if (!obj->IsJSObject()) return Heap::null_value();
497 return JSObject::cast(obj)->class_name();
498}
499
ager@chromium.org7c537e22008-10-16 08:43:32 +0000500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000501static Object* Runtime_IsInPrototypeChain(Arguments args) {
502 NoHandleAllocation ha;
503 ASSERT(args.length() == 2);
504 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
505 Object* O = args[0];
506 Object* V = args[1];
507 while (true) {
508 Object* prototype = V->GetPrototype();
509 if (prototype->IsNull()) return Heap::false_value();
510 if (O == prototype) return Heap::true_value();
511 V = prototype;
512 }
513}
514
515
ager@chromium.org9085a012009-05-11 19:22:57 +0000516// Inserts an object as the hidden prototype of another object.
517static Object* Runtime_SetHiddenPrototype(Arguments args) {
518 NoHandleAllocation ha;
519 ASSERT(args.length() == 2);
520 CONVERT_CHECKED(JSObject, jsobject, args[0]);
521 CONVERT_CHECKED(JSObject, proto, args[1]);
522
523 // Sanity checks. The old prototype (that we are replacing) could
524 // theoretically be null, but if it is not null then check that we
525 // didn't already install a hidden prototype here.
526 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
527 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
528 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
529
530 // Allocate up front before we start altering state in case we get a GC.
531 Object* map_or_failure = proto->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_proto_map = Map::cast(map_or_failure);
534
535 map_or_failure = jsobject->map()->CopyDropTransitions();
536 if (map_or_failure->IsFailure()) return map_or_failure;
537 Map* new_map = Map::cast(map_or_failure);
538
539 // Set proto's prototype to be the old prototype of the object.
540 new_proto_map->set_prototype(jsobject->GetPrototype());
541 proto->set_map(new_proto_map);
542 new_proto_map->set_is_hidden_prototype();
543
544 // Set the object's prototype to proto.
545 new_map->set_prototype(proto);
546 jsobject->set_map(new_map);
547
548 return Heap::undefined_value();
549}
550
551
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000552static Object* Runtime_IsConstructCall(Arguments args) {
553 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000554 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555 JavaScriptFrameIterator it;
556 return Heap::ToBoolean(it.frame()->IsConstructor());
557}
558
559
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000560// Recursively traverses hidden prototypes if property is not found
561static void GetOwnPropertyImplementation(JSObject* obj,
562 String* name,
563 LookupResult* result) {
564 obj->LocalLookupRealNamedProperty(name, result);
565
566 if (!result->IsProperty()) {
567 Object* proto = obj->GetPrototype();
568 if (proto->IsJSObject() &&
569 JSObject::cast(proto)->map()->is_hidden_prototype())
570 GetOwnPropertyImplementation(JSObject::cast(proto),
571 name, result);
572 }
573}
574
575
576// Returns an array with the property description:
577// if args[1] is not a property on args[0]
578// returns undefined
579// if args[1] is a data property on args[0]
580// [false, value, Writeable, Enumerable, Configurable]
581// if args[1] is an accessor on args[0]
582// [true, GetFunction, SetFunction, Enumerable, Configurable]
583static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000584 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000585 HandleScope scope;
586 Handle<FixedArray> elms = Factory::NewFixedArray(5);
587 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
588 LookupResult result;
589 CONVERT_CHECKED(JSObject, obj, args[0]);
590 CONVERT_CHECKED(String, name, args[1]);
591
592 // Use recursive implementation to also traverse hidden prototypes
593 GetOwnPropertyImplementation(obj, name, &result);
594
595 if (!result.IsProperty())
596 return Heap::undefined_value();
597
598 if (result.type() == CALLBACKS) {
599 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000600 if (structure->IsProxy() || structure->IsAccessorInfo()) {
601 // Property that is internally implemented as a callback or
602 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000603 Object* value = obj->GetPropertyWithCallback(
604 obj, structure, name, result.holder());
605 elms->set(0, Heap::false_value());
606 elms->set(1, value);
607 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
608 } else if (structure->IsFixedArray()) {
609 // __defineGetter__/__defineSetter__ callback.
610 elms->set(0, Heap::true_value());
611 elms->set(1, FixedArray::cast(structure)->get(0));
612 elms->set(2, FixedArray::cast(structure)->get(1));
613 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000614 return Heap::undefined_value();
615 }
616 } else {
617 elms->set(0, Heap::false_value());
618 elms->set(1, result.GetLazyValue());
619 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
620 }
621
622 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000623 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000624 return *desc;
625}
626
627
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000628static Object* Runtime_IsExtensible(Arguments args) {
629 ASSERT(args.length() == 1);
630 CONVERT_CHECKED(JSObject, obj, args[0]);
631 return obj->map()->is_extensible() ? Heap::true_value()
632 : Heap::false_value();
633}
634
635
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000636static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000637 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000638 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000639 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
640 CONVERT_ARG_CHECKED(String, pattern, 1);
641 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000642 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
643 if (result.is_null()) return Failure::Exception();
644 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000645}
646
647
648static Object* Runtime_CreateApiFunction(Arguments args) {
649 HandleScope scope;
650 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000651 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000652 return *Factory::CreateApiFunction(data);
653}
654
655
656static Object* Runtime_IsTemplate(Arguments args) {
657 ASSERT(args.length() == 1);
658 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000659 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000660 return Heap::ToBoolean(result);
661}
662
663
664static Object* Runtime_GetTemplateField(Arguments args) {
665 ASSERT(args.length() == 2);
666 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000667 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000668 int index = field->value();
669 int offset = index * kPointerSize + HeapObject::kHeaderSize;
670 InstanceType type = templ->map()->instance_type();
671 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
672 type == OBJECT_TEMPLATE_INFO_TYPE);
673 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000674 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000675 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
676 } else {
677 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
678 }
679 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000680}
681
682
ager@chromium.org870a0b62008-11-04 11:43:05 +0000683static Object* Runtime_DisableAccessChecks(Arguments args) {
684 ASSERT(args.length() == 1);
685 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000686 Map* old_map = object->map();
687 bool needs_access_checks = old_map->is_access_check_needed();
688 if (needs_access_checks) {
689 // Copy map so it won't interfere constructor's initial map.
690 Object* new_map = old_map->CopyDropTransitions();
691 if (new_map->IsFailure()) return new_map;
692
693 Map::cast(new_map)->set_is_access_check_needed(false);
694 object->set_map(Map::cast(new_map));
695 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000696 return needs_access_checks ? Heap::true_value() : Heap::false_value();
697}
698
699
700static Object* Runtime_EnableAccessChecks(Arguments args) {
701 ASSERT(args.length() == 1);
702 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000703 Map* old_map = object->map();
704 if (!old_map->is_access_check_needed()) {
705 // Copy map so it won't interfere constructor's initial map.
706 Object* new_map = old_map->CopyDropTransitions();
707 if (new_map->IsFailure()) return new_map;
708
709 Map::cast(new_map)->set_is_access_check_needed(true);
710 object->set_map(Map::cast(new_map));
711 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000712 return Heap::undefined_value();
713}
714
715
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000716static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
717 HandleScope scope;
718 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
719 Handle<Object> args[2] = { type_handle, name };
720 Handle<Object> error =
721 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
722 return Top::Throw(*error);
723}
724
725
726static Object* Runtime_DeclareGlobals(Arguments args) {
727 HandleScope scope;
728 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
729
ager@chromium.org3811b432009-10-28 14:53:37 +0000730 Handle<Context> context = args.at<Context>(0);
731 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000732 bool is_eval = Smi::cast(args[2])->value() == 1;
733
734 // Compute the property attributes. According to ECMA-262, section
735 // 13, page 71, the property must be read-only and
736 // non-deletable. However, neither SpiderMonkey nor KJS creates the
737 // property as read-only, so we don't either.
738 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
739
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000740 // Traverse the name/value pairs and set the properties.
741 int length = pairs->length();
742 for (int i = 0; i < length; i += 2) {
743 HandleScope scope;
744 Handle<String> name(String::cast(pairs->get(i)));
745 Handle<Object> value(pairs->get(i + 1));
746
747 // We have to declare a global const property. To capture we only
748 // assign to it when evaluating the assignment for "const x =
749 // <expr>" the initial value is the hole.
750 bool is_const_property = value->IsTheHole();
751
752 if (value->IsUndefined() || is_const_property) {
753 // Lookup the property in the global object, and don't set the
754 // value of the variable if the property is already there.
755 LookupResult lookup;
756 global->Lookup(*name, &lookup);
757 if (lookup.IsProperty()) {
758 // Determine if the property is local by comparing the holder
759 // against the global object. The information will be used to
760 // avoid throwing re-declaration errors when declaring
761 // variables or constants that exist in the prototype chain.
762 bool is_local = (*global == lookup.holder());
763 // Get the property attributes and determine if the property is
764 // read-only.
765 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
766 bool is_read_only = (attributes & READ_ONLY) != 0;
767 if (lookup.type() == INTERCEPTOR) {
768 // If the interceptor says the property is there, we
769 // just return undefined without overwriting the property.
770 // Otherwise, we continue to setting the property.
771 if (attributes != ABSENT) {
772 // Check if the existing property conflicts with regards to const.
773 if (is_local && (is_read_only || is_const_property)) {
774 const char* type = (is_read_only) ? "const" : "var";
775 return ThrowRedeclarationError(type, name);
776 };
777 // The property already exists without conflicting: Go to
778 // the next declaration.
779 continue;
780 }
781 // Fall-through and introduce the absent property by using
782 // SetProperty.
783 } else {
784 if (is_local && (is_read_only || is_const_property)) {
785 const char* type = (is_read_only) ? "const" : "var";
786 return ThrowRedeclarationError(type, name);
787 }
788 // The property already exists without conflicting: Go to
789 // the next declaration.
790 continue;
791 }
792 }
793 } else {
794 // Copy the function and update its context. Use it as value.
795 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
796 Handle<JSFunction> function =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +0000797 Factory::NewFunctionFromBoilerplate(boilerplate, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000798 value = function;
799 }
800
801 LookupResult lookup;
802 global->LocalLookup(*name, &lookup);
803
804 PropertyAttributes attributes = is_const_property
805 ? static_cast<PropertyAttributes>(base | READ_ONLY)
806 : base;
807
808 if (lookup.IsProperty()) {
809 // There's a local property that we need to overwrite because
810 // we're either declaring a function or there's an interceptor
811 // that claims the property is absent.
812
813 // Check for conflicting re-declarations. We cannot have
814 // conflicting types in case of intercepted properties because
815 // they are absent.
816 if (lookup.type() != INTERCEPTOR &&
817 (lookup.IsReadOnly() || is_const_property)) {
818 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
819 return ThrowRedeclarationError(type, name);
820 }
821 SetProperty(global, name, value, attributes);
822 } else {
823 // If a property with this name does not already exist on the
824 // global object add the property locally. We take special
825 // precautions to always add it as a local property even in case
826 // of callbacks in the prototype chain (this rules out using
827 // SetProperty). Also, we must use the handle-based version to
828 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000829 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 }
831 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000832
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833 return Heap::undefined_value();
834}
835
836
837static Object* Runtime_DeclareContextSlot(Arguments args) {
838 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000839 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840
ager@chromium.org7c537e22008-10-16 08:43:32 +0000841 CONVERT_ARG_CHECKED(Context, context, 0);
842 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000843 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000844 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000845 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000846 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000847
848 // Declarations are always done in the function context.
849 context = Handle<Context>(context->fcontext());
850
851 int index;
852 PropertyAttributes attributes;
853 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000854 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000855 context->Lookup(name, flags, &index, &attributes);
856
857 if (attributes != ABSENT) {
858 // The name was declared before; check for conflicting
859 // re-declarations: This is similar to the code in parser.cc in
860 // the AstBuildingParser::Declare function.
861 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
862 // Functions are not read-only.
863 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
864 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
865 return ThrowRedeclarationError(type, name);
866 }
867
868 // Initialize it if necessary.
869 if (*initial_value != NULL) {
870 if (index >= 0) {
871 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000872 // the function context or the arguments object.
873 if (holder->IsContext()) {
874 ASSERT(holder.is_identical_to(context));
875 if (((attributes & READ_ONLY) == 0) ||
876 context->get(index)->IsTheHole()) {
877 context->set(index, *initial_value);
878 }
879 } else {
880 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000881 }
882 } else {
883 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000884 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885 SetProperty(context_ext, name, initial_value, mode);
886 }
887 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000888
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000889 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000890 // The property is not in the function context. It needs to be
891 // "declared" in the function context's extension context, or in the
892 // global context.
893 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000894 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000895 // The function context's extension context exists - use it.
896 context_ext = Handle<JSObject>(context->extension());
897 } else {
898 // The function context's extension context does not exists - allocate
899 // it.
900 context_ext = Factory::NewJSObject(Top::context_extension_function());
901 // And store it in the extension slot.
902 context->set_extension(*context_ext);
903 }
904 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000905
ager@chromium.org7c537e22008-10-16 08:43:32 +0000906 // Declare the property by setting it to the initial value if provided,
907 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
908 // constant declarations).
909 ASSERT(!context_ext->HasLocalProperty(*name));
910 Handle<Object> value(Heap::undefined_value());
911 if (*initial_value != NULL) value = initial_value;
912 SetProperty(context_ext, name, value, mode);
913 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
914 }
915
916 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000917}
918
919
920static Object* Runtime_InitializeVarGlobal(Arguments args) {
921 NoHandleAllocation nha;
922
923 // Determine if we need to assign to the variable if it already
924 // exists (based on the number of arguments).
925 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
926 bool assign = args.length() == 2;
927
928 CONVERT_ARG_CHECKED(String, name, 0);
929 GlobalObject* global = Top::context()->global();
930
931 // According to ECMA-262, section 12.2, page 62, the property must
932 // not be deletable.
933 PropertyAttributes attributes = DONT_DELETE;
934
935 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000936 // there, there is a property with this name in the prototype chain.
937 // We follow Safari and Firefox behavior and only set the property
938 // locally if there is an explicit initialization value that we have
939 // to assign to the property. When adding the property we take
940 // special precautions to always add it as a local property even in
941 // case of callbacks in the prototype chain (this rules out using
942 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
943 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000944 // Note that objects can have hidden prototypes, so we need to traverse
945 // the whole chain of hidden prototypes to do a 'local' lookup.
946 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000947 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000948 while (true) {
949 real_holder->LocalLookup(*name, &lookup);
950 if (lookup.IsProperty()) {
951 // Determine if this is a redeclaration of something read-only.
952 if (lookup.IsReadOnly()) {
953 // If we found readonly property on one of hidden prototypes,
954 // just shadow it.
955 if (real_holder != Top::context()->global()) break;
956 return ThrowRedeclarationError("const", name);
957 }
958
959 // Determine if this is a redeclaration of an intercepted read-only
960 // property and figure out if the property exists at all.
961 bool found = true;
962 PropertyType type = lookup.type();
963 if (type == INTERCEPTOR) {
964 HandleScope handle_scope;
965 Handle<JSObject> holder(real_holder);
966 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
967 real_holder = *holder;
968 if (intercepted == ABSENT) {
969 // The interceptor claims the property isn't there. We need to
970 // make sure to introduce it.
971 found = false;
972 } else if ((intercepted & READ_ONLY) != 0) {
973 // The property is present, but read-only. Since we're trying to
974 // overwrite it with a variable declaration we must throw a
975 // re-declaration error. However if we found readonly property
976 // on one of hidden prototypes, just shadow it.
977 if (real_holder != Top::context()->global()) break;
978 return ThrowRedeclarationError("const", name);
979 }
980 }
981
982 if (found && !assign) {
983 // The global property is there and we're not assigning any value
984 // to it. Just return.
985 return Heap::undefined_value();
986 }
987
988 // Assign the value (or undefined) to the property.
989 Object* value = (assign) ? args[1] : Heap::undefined_value();
990 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000991 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000992
993 Object* proto = real_holder->GetPrototype();
994 if (!proto->IsJSObject())
995 break;
996
997 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
998 break;
999
1000 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 }
1002
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001003 global = Top::context()->global();
1004 if (assign) {
1005 return global->IgnoreAttributesAndSetLocalProperty(*name,
1006 args[1],
1007 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001008 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001009 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001010}
1011
1012
1013static Object* Runtime_InitializeConstGlobal(Arguments args) {
1014 // All constants are declared with an initial value. The name
1015 // of the constant is the first argument and the initial value
1016 // is the second.
1017 RUNTIME_ASSERT(args.length() == 2);
1018 CONVERT_ARG_CHECKED(String, name, 0);
1019 Handle<Object> value = args.at<Object>(1);
1020
1021 // Get the current global object from top.
1022 GlobalObject* global = Top::context()->global();
1023
1024 // According to ECMA-262, section 12.2, page 62, the property must
1025 // not be deletable. Since it's a const, it must be READ_ONLY too.
1026 PropertyAttributes attributes =
1027 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1028
1029 // Lookup the property locally in the global object. If it isn't
1030 // there, we add the property and take special precautions to always
1031 // add it as a local property even in case of callbacks in the
1032 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001033 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001034 LookupResult lookup;
1035 global->LocalLookup(*name, &lookup);
1036 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001037 return global->IgnoreAttributesAndSetLocalProperty(*name,
1038 *value,
1039 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040 }
1041
1042 // Determine if this is a redeclaration of something not
1043 // read-only. In case the result is hidden behind an interceptor we
1044 // need to ask it for the property attributes.
1045 if (!lookup.IsReadOnly()) {
1046 if (lookup.type() != INTERCEPTOR) {
1047 return ThrowRedeclarationError("var", name);
1048 }
1049
1050 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1051
1052 // Throw re-declaration error if the intercepted property is present
1053 // but not read-only.
1054 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1055 return ThrowRedeclarationError("var", name);
1056 }
1057
1058 // Restore global object from context (in case of GC) and continue
1059 // with setting the value because the property is either absent or
1060 // read-only. We also have to do redo the lookup.
1061 global = Top::context()->global();
1062
1063 // BUG 1213579: Handle the case where we have to set a read-only
1064 // property through an interceptor and only do it if it's
1065 // uninitialized, e.g. the hole. Nirk...
1066 global->SetProperty(*name, *value, attributes);
1067 return *value;
1068 }
1069
1070 // Set the value, but only we're assigning the initial value to a
1071 // constant. For now, we determine this by checking if the
1072 // current value is the hole.
1073 PropertyType type = lookup.type();
1074 if (type == FIELD) {
1075 FixedArray* properties = global->properties();
1076 int index = lookup.GetFieldIndex();
1077 if (properties->get(index)->IsTheHole()) {
1078 properties->set(index, *value);
1079 }
1080 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001081 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1082 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001083 }
1084 } else {
1085 // Ignore re-initialization of constants that have already been
1086 // assigned a function value.
1087 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1088 }
1089
1090 // Use the set value as the result of the operation.
1091 return *value;
1092}
1093
1094
1095static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1096 HandleScope scope;
1097 ASSERT(args.length() == 3);
1098
1099 Handle<Object> value(args[0]);
1100 ASSERT(!value->IsTheHole());
1101 CONVERT_ARG_CHECKED(Context, context, 1);
1102 Handle<String> name(String::cast(args[2]));
1103
1104 // Initializations are always done in the function context.
1105 context = Handle<Context>(context->fcontext());
1106
1107 int index;
1108 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001109 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001110 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001111 context->Lookup(name, flags, &index, &attributes);
1112
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001113 // In most situations, the property introduced by the const
1114 // declaration should be present in the context extension object.
1115 // However, because declaration and initialization are separate, the
1116 // property might have been deleted (if it was introduced by eval)
1117 // before we reach the initialization point.
1118 //
1119 // Example:
1120 //
1121 // function f() { eval("delete x; const x;"); }
1122 //
1123 // In that case, the initialization behaves like a normal assignment
1124 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001125 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001126 // Property was found in a context.
1127 if (holder->IsContext()) {
1128 // The holder cannot be the function context. If it is, there
1129 // should have been a const redeclaration error when declaring
1130 // the const property.
1131 ASSERT(!holder.is_identical_to(context));
1132 if ((attributes & READ_ONLY) == 0) {
1133 Handle<Context>::cast(holder)->set(index, *value);
1134 }
1135 } else {
1136 // The holder is an arguments object.
1137 ASSERT((attributes & READ_ONLY) == 0);
1138 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001139 }
1140 return *value;
1141 }
1142
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001143 // The property could not be found, we introduce it in the global
1144 // context.
1145 if (attributes == ABSENT) {
1146 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1147 SetProperty(global, name, value, NONE);
1148 return *value;
1149 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001151 // The property was present in a context extension object.
1152 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001153
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001154 if (*context_ext == context->extension()) {
1155 // This is the property that was introduced by the const
1156 // declaration. Set it if it hasn't been set before. NOTE: We
1157 // cannot use GetProperty() to get the current value as it
1158 // 'unholes' the value.
1159 LookupResult lookup;
1160 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1161 ASSERT(lookup.IsProperty()); // the property was declared
1162 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1163
1164 PropertyType type = lookup.type();
1165 if (type == FIELD) {
1166 FixedArray* properties = context_ext->properties();
1167 int index = lookup.GetFieldIndex();
1168 if (properties->get(index)->IsTheHole()) {
1169 properties->set(index, *value);
1170 }
1171 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001172 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1173 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001174 }
1175 } else {
1176 // We should not reach here. Any real, named property should be
1177 // either a field or a dictionary slot.
1178 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001179 }
1180 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001181 // The property was found in a different context extension object.
1182 // Set it if it is not a read-only property.
1183 if ((attributes & READ_ONLY) == 0) {
1184 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1185 // Setting a property might throw an exception. Exceptions
1186 // are converted to empty handles in handle operations. We
1187 // need to convert back to exceptions here.
1188 if (set.is_null()) {
1189 ASSERT(Top::has_pending_exception());
1190 return Failure::Exception();
1191 }
1192 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001193 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001194
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001195 return *value;
1196}
1197
1198
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001199static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1200 Arguments args) {
1201 HandleScope scope;
1202 ASSERT(args.length() == 2);
1203 CONVERT_ARG_CHECKED(JSObject, object, 0);
1204 CONVERT_SMI_CHECKED(properties, args[1]);
1205 if (object->HasFastProperties()) {
1206 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1207 }
1208 return *object;
1209}
1210
1211
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001212static Object* Runtime_RegExpExec(Arguments args) {
1213 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001214 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001215 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1216 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001217 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001218 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001219 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001220 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001221 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001222 RUNTIME_ASSERT(index >= 0);
1223 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001224 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001225 Handle<Object> result = RegExpImpl::Exec(regexp,
1226 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001227 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001228 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001229 if (result.is_null()) return Failure::Exception();
1230 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001231}
1232
1233
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001234static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1235 HandleScope scope;
1236 ASSERT(args.length() == 1);
1237 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1238 // This is necessary to enable fast checks for absence of elements
1239 // on Array.prototype and below.
1240 prototype->set_elements(Heap::empty_fixed_array());
1241 return Smi::FromInt(0);
1242}
1243
1244
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001245static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1246 HandleScope scope;
1247 ASSERT(args.length() == 4);
1248 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1249 int index = Smi::cast(args[1])->value();
1250 Handle<String> pattern = args.at<String>(2);
1251 Handle<String> flags = args.at<String>(3);
1252
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001253 // Get the RegExp function from the context in the literals array.
1254 // This is the RegExp function from the context in which the
1255 // function was created. We do not use the RegExp function from the
1256 // current global context because this might be the RegExp function
1257 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001258 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001259 Handle<JSFunction>(
1260 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001261 // Compute the regular expression literal.
1262 bool has_pending_exception;
1263 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001264 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1265 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001266 if (has_pending_exception) {
1267 ASSERT(Top::has_pending_exception());
1268 return Failure::Exception();
1269 }
1270 literals->set(index, *regexp);
1271 return *regexp;
1272}
1273
1274
1275static Object* Runtime_FunctionGetName(Arguments args) {
1276 NoHandleAllocation ha;
1277 ASSERT(args.length() == 1);
1278
1279 CONVERT_CHECKED(JSFunction, f, args[0]);
1280 return f->shared()->name();
1281}
1282
1283
ager@chromium.org236ad962008-09-25 09:45:57 +00001284static Object* Runtime_FunctionSetName(Arguments args) {
1285 NoHandleAllocation ha;
1286 ASSERT(args.length() == 2);
1287
1288 CONVERT_CHECKED(JSFunction, f, args[0]);
1289 CONVERT_CHECKED(String, name, args[1]);
1290 f->shared()->set_name(name);
1291 return Heap::undefined_value();
1292}
1293
1294
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001295static Object* Runtime_FunctionGetScript(Arguments args) {
1296 HandleScope scope;
1297 ASSERT(args.length() == 1);
1298
1299 CONVERT_CHECKED(JSFunction, fun, args[0]);
1300 Handle<Object> script = Handle<Object>(fun->shared()->script());
1301 if (!script->IsScript()) return Heap::undefined_value();
1302
1303 return *GetScriptWrapper(Handle<Script>::cast(script));
1304}
1305
1306
1307static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1308 NoHandleAllocation ha;
1309 ASSERT(args.length() == 1);
1310
1311 CONVERT_CHECKED(JSFunction, f, args[0]);
1312 return f->shared()->GetSourceCode();
1313}
1314
1315
1316static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1317 NoHandleAllocation ha;
1318 ASSERT(args.length() == 1);
1319
1320 CONVERT_CHECKED(JSFunction, fun, args[0]);
1321 int pos = fun->shared()->start_position();
1322 return Smi::FromInt(pos);
1323}
1324
1325
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001326static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1327 ASSERT(args.length() == 2);
1328
1329 CONVERT_CHECKED(JSFunction, fun, args[0]);
1330 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1331
1332 Code* code = fun->code();
1333 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1334
1335 Address pc = code->address() + offset;
1336 return Smi::FromInt(fun->code()->SourcePosition(pc));
1337}
1338
1339
1340
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001341static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1342 NoHandleAllocation ha;
1343 ASSERT(args.length() == 2);
1344
1345 CONVERT_CHECKED(JSFunction, fun, args[0]);
1346 CONVERT_CHECKED(String, name, args[1]);
1347 fun->SetInstanceClassName(name);
1348 return Heap::undefined_value();
1349}
1350
1351
1352static Object* Runtime_FunctionSetLength(Arguments args) {
1353 NoHandleAllocation ha;
1354 ASSERT(args.length() == 2);
1355
1356 CONVERT_CHECKED(JSFunction, fun, args[0]);
1357 CONVERT_CHECKED(Smi, length, args[1]);
1358 fun->shared()->set_length(length->value());
1359 return length;
1360}
1361
1362
1363static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001364 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001365 ASSERT(args.length() == 2);
1366
1367 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001368 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1369 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001370 return args[0]; // return TOS
1371}
1372
1373
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001374static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1375 NoHandleAllocation ha;
1376 ASSERT(args.length() == 1);
1377
1378 CONVERT_CHECKED(JSFunction, f, args[0]);
1379 // The function_data field of the shared function info is used exclusively by
1380 // the API.
1381 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1382 : Heap::false_value();
1383}
1384
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001385static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1386 NoHandleAllocation ha;
1387 ASSERT(args.length() == 1);
1388
1389 CONVERT_CHECKED(JSFunction, f, args[0]);
1390 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1391}
1392
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001393
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001394static Object* Runtime_SetCode(Arguments args) {
1395 HandleScope scope;
1396 ASSERT(args.length() == 2);
1397
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001398 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001399 Handle<Object> code = args.at<Object>(1);
1400
1401 Handle<Context> context(target->context());
1402
1403 if (!code->IsNull()) {
1404 RUNTIME_ASSERT(code->IsJSFunction());
1405 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001406 Handle<SharedFunctionInfo> shared(fun->shared());
1407 SetExpectedNofProperties(target, shared->expected_nof_properties());
1408
1409 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001410 return Failure::Exception();
1411 }
1412 // Set the code, formal parameter count, and the length of the target
1413 // function.
1414 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001415 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001416 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001417 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001418 // Set the source code of the target function to undefined.
1419 // SetCode is only used for built-in constructors like String,
1420 // Array, and Object, and some web code
1421 // doesn't like seeing source code for constructors.
1422 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001423 // Clear the optimization hints related to the compiled code as these are no
1424 // longer valid when the code is overwritten.
1425 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001426 context = Handle<Context>(fun->context());
1427
1428 // Make sure we get a fresh copy of the literal vector to avoid
1429 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001430 int number_of_literals = fun->NumberOfLiterals();
1431 Handle<FixedArray> literals =
1432 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001433 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001434 // Insert the object, regexp and array functions in the literals
1435 // array prefix. These are the functions that will be used when
1436 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001437 literals->set(JSFunction::kLiteralGlobalContextIndex,
1438 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001439 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001440 // It's okay to skip the write barrier here because the literals
1441 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001442 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001443 }
1444
1445 target->set_context(*context);
1446 return *target;
1447}
1448
1449
1450static Object* CharCodeAt(String* subject, Object* index) {
1451 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001452 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001453 // Flatten the string. If someone wants to get a char at an index
1454 // in a cons string, it is likely that more indices will be
1455 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001456 Object* flat = subject->TryFlatten();
1457 if (flat->IsFailure()) return flat;
1458 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001459 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001460 return Heap::nan_value();
1461 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001462 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001463}
1464
1465
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001466static Object* CharFromCode(Object* char_code) {
1467 uint32_t code;
1468 if (Array::IndexFromObject(char_code, &code)) {
1469 if (code <= 0xffff) {
1470 return Heap::LookupSingleCharacterStringFromCode(code);
1471 }
1472 }
1473 return Heap::empty_string();
1474}
1475
1476
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001477static Object* Runtime_StringCharCodeAt(Arguments args) {
1478 NoHandleAllocation ha;
1479 ASSERT(args.length() == 2);
1480
1481 CONVERT_CHECKED(String, subject, args[0]);
1482 Object* index = args[1];
1483 return CharCodeAt(subject, index);
1484}
1485
1486
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001487static Object* Runtime_StringCharAt(Arguments args) {
1488 NoHandleAllocation ha;
1489 ASSERT(args.length() == 2);
1490
1491 CONVERT_CHECKED(String, subject, args[0]);
1492 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001493 Object* code = CharCodeAt(subject, index);
1494 if (code == Heap::nan_value()) {
1495 return Heap::undefined_value();
1496 }
1497 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001498}
1499
1500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001501static Object* Runtime_CharFromCode(Arguments args) {
1502 NoHandleAllocation ha;
1503 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001504 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001505}
1506
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001507// Forward declarations.
1508static const int kStringBuilderConcatHelperLengthBits = 11;
1509static const int kStringBuilderConcatHelperPositionBits = 19;
1510
1511template <typename schar>
1512static inline void StringBuilderConcatHelper(String*,
1513 schar*,
1514 FixedArray*,
1515 int);
1516
1517typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1518typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1519
1520class ReplacementStringBuilder {
1521 public:
1522 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1523 : subject_(subject),
1524 parts_(Factory::NewFixedArray(estimated_part_count)),
1525 part_count_(0),
1526 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001527 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001528 // Require a non-zero initial size. Ensures that doubling the size to
1529 // extend the array will work.
1530 ASSERT(estimated_part_count > 0);
1531 }
1532
1533 void EnsureCapacity(int elements) {
1534 int length = parts_->length();
1535 int required_length = part_count_ + elements;
1536 if (length < required_length) {
1537 int new_length = length;
1538 do {
1539 new_length *= 2;
1540 } while (new_length < required_length);
1541 Handle<FixedArray> extended_array =
1542 Factory::NewFixedArray(new_length);
1543 parts_->CopyTo(0, *extended_array, 0, part_count_);
1544 parts_ = extended_array;
1545 }
1546 }
1547
1548 void AddSubjectSlice(int from, int to) {
1549 ASSERT(from >= 0);
1550 int length = to - from;
1551 ASSERT(length > 0);
1552 // Can we encode the slice in 11 bits for length and 19 bits for
1553 // start position - as used by StringBuilderConcatHelper?
1554 if (StringBuilderSubstringLength::is_valid(length) &&
1555 StringBuilderSubstringPosition::is_valid(from)) {
1556 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1557 StringBuilderSubstringPosition::encode(from);
1558 AddElement(Smi::FromInt(encoded_slice));
1559 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001560 // Otherwise encode as two smis.
1561 AddElement(Smi::FromInt(-length));
1562 AddElement(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001563 }
1564 IncrementCharacterCount(length);
1565 }
1566
1567
1568 void AddString(Handle<String> string) {
1569 int length = string->length();
1570 ASSERT(length > 0);
1571 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001572 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001573 is_ascii_ = false;
1574 }
1575 IncrementCharacterCount(length);
1576 }
1577
1578
1579 Handle<String> ToString() {
1580 if (part_count_ == 0) {
1581 return Factory::empty_string();
1582 }
1583
1584 Handle<String> joined_string;
1585 if (is_ascii_) {
1586 joined_string = NewRawAsciiString(character_count_);
1587 AssertNoAllocation no_alloc;
1588 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1589 char* char_buffer = seq->GetChars();
1590 StringBuilderConcatHelper(*subject_,
1591 char_buffer,
1592 *parts_,
1593 part_count_);
1594 } else {
1595 // Non-ASCII.
1596 joined_string = NewRawTwoByteString(character_count_);
1597 AssertNoAllocation no_alloc;
1598 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1599 uc16* char_buffer = seq->GetChars();
1600 StringBuilderConcatHelper(*subject_,
1601 char_buffer,
1602 *parts_,
1603 part_count_);
1604 }
1605 return joined_string;
1606 }
1607
1608
1609 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001610 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001611 V8::FatalProcessOutOfMemory("String.replace result too large.");
1612 }
1613 character_count_ += by;
1614 }
1615
1616 private:
1617
1618 Handle<String> NewRawAsciiString(int size) {
1619 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1620 }
1621
1622
1623 Handle<String> NewRawTwoByteString(int size) {
1624 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1625 }
1626
1627
1628 void AddElement(Object* element) {
1629 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001630 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001631 parts_->set(part_count_, element);
1632 part_count_++;
1633 }
1634
1635 Handle<String> subject_;
1636 Handle<FixedArray> parts_;
1637 int part_count_;
1638 int character_count_;
1639 bool is_ascii_;
1640};
1641
1642
1643class CompiledReplacement {
1644 public:
1645 CompiledReplacement()
1646 : parts_(1), replacement_substrings_(0) {}
1647
1648 void Compile(Handle<String> replacement,
1649 int capture_count,
1650 int subject_length);
1651
1652 void Apply(ReplacementStringBuilder* builder,
1653 int match_from,
1654 int match_to,
1655 Handle<JSArray> last_match_info);
1656
1657 // Number of distinct parts of the replacement pattern.
1658 int parts() {
1659 return parts_.length();
1660 }
1661 private:
1662 enum PartType {
1663 SUBJECT_PREFIX = 1,
1664 SUBJECT_SUFFIX,
1665 SUBJECT_CAPTURE,
1666 REPLACEMENT_SUBSTRING,
1667 REPLACEMENT_STRING,
1668
1669 NUMBER_OF_PART_TYPES
1670 };
1671
1672 struct ReplacementPart {
1673 static inline ReplacementPart SubjectMatch() {
1674 return ReplacementPart(SUBJECT_CAPTURE, 0);
1675 }
1676 static inline ReplacementPart SubjectCapture(int capture_index) {
1677 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1678 }
1679 static inline ReplacementPart SubjectPrefix() {
1680 return ReplacementPart(SUBJECT_PREFIX, 0);
1681 }
1682 static inline ReplacementPart SubjectSuffix(int subject_length) {
1683 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1684 }
1685 static inline ReplacementPart ReplacementString() {
1686 return ReplacementPart(REPLACEMENT_STRING, 0);
1687 }
1688 static inline ReplacementPart ReplacementSubString(int from, int to) {
1689 ASSERT(from >= 0);
1690 ASSERT(to > from);
1691 return ReplacementPart(-from, to);
1692 }
1693
1694 // If tag <= 0 then it is the negation of a start index of a substring of
1695 // the replacement pattern, otherwise it's a value from PartType.
1696 ReplacementPart(int tag, int data)
1697 : tag(tag), data(data) {
1698 // Must be non-positive or a PartType value.
1699 ASSERT(tag < NUMBER_OF_PART_TYPES);
1700 }
1701 // Either a value of PartType or a non-positive number that is
1702 // the negation of an index into the replacement string.
1703 int tag;
1704 // The data value's interpretation depends on the value of tag:
1705 // tag == SUBJECT_PREFIX ||
1706 // tag == SUBJECT_SUFFIX: data is unused.
1707 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1708 // tag == REPLACEMENT_SUBSTRING ||
1709 // tag == REPLACEMENT_STRING: data is index into array of substrings
1710 // of the replacement string.
1711 // tag <= 0: Temporary representation of the substring of the replacement
1712 // string ranging over -tag .. data.
1713 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1714 // substring objects.
1715 int data;
1716 };
1717
1718 template<typename Char>
1719 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1720 Vector<Char> characters,
1721 int capture_count,
1722 int subject_length) {
1723 int length = characters.length();
1724 int last = 0;
1725 for (int i = 0; i < length; i++) {
1726 Char c = characters[i];
1727 if (c == '$') {
1728 int next_index = i + 1;
1729 if (next_index == length) { // No next character!
1730 break;
1731 }
1732 Char c2 = characters[next_index];
1733 switch (c2) {
1734 case '$':
1735 if (i > last) {
1736 // There is a substring before. Include the first "$".
1737 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1738 last = next_index + 1; // Continue after the second "$".
1739 } else {
1740 // Let the next substring start with the second "$".
1741 last = next_index;
1742 }
1743 i = next_index;
1744 break;
1745 case '`':
1746 if (i > last) {
1747 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1748 }
1749 parts->Add(ReplacementPart::SubjectPrefix());
1750 i = next_index;
1751 last = i + 1;
1752 break;
1753 case '\'':
1754 if (i > last) {
1755 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1756 }
1757 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1758 i = next_index;
1759 last = i + 1;
1760 break;
1761 case '&':
1762 if (i > last) {
1763 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1764 }
1765 parts->Add(ReplacementPart::SubjectMatch());
1766 i = next_index;
1767 last = i + 1;
1768 break;
1769 case '0':
1770 case '1':
1771 case '2':
1772 case '3':
1773 case '4':
1774 case '5':
1775 case '6':
1776 case '7':
1777 case '8':
1778 case '9': {
1779 int capture_ref = c2 - '0';
1780 if (capture_ref > capture_count) {
1781 i = next_index;
1782 continue;
1783 }
1784 int second_digit_index = next_index + 1;
1785 if (second_digit_index < length) {
1786 // Peek ahead to see if we have two digits.
1787 Char c3 = characters[second_digit_index];
1788 if ('0' <= c3 && c3 <= '9') { // Double digits.
1789 int double_digit_ref = capture_ref * 10 + c3 - '0';
1790 if (double_digit_ref <= capture_count) {
1791 next_index = second_digit_index;
1792 capture_ref = double_digit_ref;
1793 }
1794 }
1795 }
1796 if (capture_ref > 0) {
1797 if (i > last) {
1798 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1799 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001800 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001801 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1802 last = next_index + 1;
1803 }
1804 i = next_index;
1805 break;
1806 }
1807 default:
1808 i = next_index;
1809 break;
1810 }
1811 }
1812 }
1813 if (length > last) {
1814 if (last == 0) {
1815 parts->Add(ReplacementPart::ReplacementString());
1816 } else {
1817 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1818 }
1819 }
1820 }
1821
1822 ZoneList<ReplacementPart> parts_;
1823 ZoneList<Handle<String> > replacement_substrings_;
1824};
1825
1826
1827void CompiledReplacement::Compile(Handle<String> replacement,
1828 int capture_count,
1829 int subject_length) {
1830 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001831 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001832 AssertNoAllocation no_alloc;
1833 ParseReplacementPattern(&parts_,
1834 replacement->ToAsciiVector(),
1835 capture_count,
1836 subject_length);
1837 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001838 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001839 AssertNoAllocation no_alloc;
1840
1841 ParseReplacementPattern(&parts_,
1842 replacement->ToUC16Vector(),
1843 capture_count,
1844 subject_length);
1845 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001846 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001847 int substring_index = 0;
1848 for (int i = 0, n = parts_.length(); i < n; i++) {
1849 int tag = parts_[i].tag;
1850 if (tag <= 0) { // A replacement string slice.
1851 int from = -tag;
1852 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001853 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001854 parts_[i].tag = REPLACEMENT_SUBSTRING;
1855 parts_[i].data = substring_index;
1856 substring_index++;
1857 } else if (tag == REPLACEMENT_STRING) {
1858 replacement_substrings_.Add(replacement);
1859 parts_[i].data = substring_index;
1860 substring_index++;
1861 }
1862 }
1863}
1864
1865
1866void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1867 int match_from,
1868 int match_to,
1869 Handle<JSArray> last_match_info) {
1870 for (int i = 0, n = parts_.length(); i < n; i++) {
1871 ReplacementPart part = parts_[i];
1872 switch (part.tag) {
1873 case SUBJECT_PREFIX:
1874 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1875 break;
1876 case SUBJECT_SUFFIX: {
1877 int subject_length = part.data;
1878 if (match_to < subject_length) {
1879 builder->AddSubjectSlice(match_to, subject_length);
1880 }
1881 break;
1882 }
1883 case SUBJECT_CAPTURE: {
1884 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001885 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001886 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1887 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1888 if (from >= 0 && to > from) {
1889 builder->AddSubjectSlice(from, to);
1890 }
1891 break;
1892 }
1893 case REPLACEMENT_SUBSTRING:
1894 case REPLACEMENT_STRING:
1895 builder->AddString(replacement_substrings_[part.data]);
1896 break;
1897 default:
1898 UNREACHABLE();
1899 }
1900 }
1901}
1902
1903
1904
1905static Object* StringReplaceRegExpWithString(String* subject,
1906 JSRegExp* regexp,
1907 String* replacement,
1908 JSArray* last_match_info) {
1909 ASSERT(subject->IsFlat());
1910 ASSERT(replacement->IsFlat());
1911
1912 HandleScope handles;
1913
1914 int length = subject->length();
1915 Handle<String> subject_handle(subject);
1916 Handle<JSRegExp> regexp_handle(regexp);
1917 Handle<String> replacement_handle(replacement);
1918 Handle<JSArray> last_match_info_handle(last_match_info);
1919 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1920 subject_handle,
1921 0,
1922 last_match_info_handle);
1923 if (match.is_null()) {
1924 return Failure::Exception();
1925 }
1926 if (match->IsNull()) {
1927 return *subject_handle;
1928 }
1929
1930 int capture_count = regexp_handle->CaptureCount();
1931
1932 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001933 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001934 CompiledReplacement compiled_replacement;
1935 compiled_replacement.Compile(replacement_handle,
1936 capture_count,
1937 length);
1938
1939 bool is_global = regexp_handle->GetFlags().is_global();
1940
1941 // Guessing the number of parts that the final result string is built
1942 // from. Global regexps can match any number of times, so we guess
1943 // conservatively.
1944 int expected_parts =
1945 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1946 ReplacementStringBuilder builder(subject_handle, expected_parts);
1947
1948 // Index of end of last match.
1949 int prev = 0;
1950
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001951 // Number of parts added by compiled replacement plus preceeding
1952 // string and possibly suffix after last match. It is possible for
1953 // all components to use two elements when encoded as two smis.
1954 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001955 bool matched = true;
1956 do {
1957 ASSERT(last_match_info_handle->HasFastElements());
1958 // Increase the capacity of the builder before entering local handle-scope,
1959 // so its internal buffer can safely allocate a new handle if it grows.
1960 builder.EnsureCapacity(parts_added_per_loop);
1961
1962 HandleScope loop_scope;
1963 int start, end;
1964 {
1965 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001966 FixedArray* match_info_array =
1967 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001968
1969 ASSERT_EQ(capture_count * 2 + 2,
1970 RegExpImpl::GetLastCaptureCount(match_info_array));
1971 start = RegExpImpl::GetCapture(match_info_array, 0);
1972 end = RegExpImpl::GetCapture(match_info_array, 1);
1973 }
1974
1975 if (prev < start) {
1976 builder.AddSubjectSlice(prev, start);
1977 }
1978 compiled_replacement.Apply(&builder,
1979 start,
1980 end,
1981 last_match_info_handle);
1982 prev = end;
1983
1984 // Only continue checking for global regexps.
1985 if (!is_global) break;
1986
1987 // Continue from where the match ended, unless it was an empty match.
1988 int next = end;
1989 if (start == end) {
1990 next = end + 1;
1991 if (next > length) break;
1992 }
1993
1994 match = RegExpImpl::Exec(regexp_handle,
1995 subject_handle,
1996 next,
1997 last_match_info_handle);
1998 if (match.is_null()) {
1999 return Failure::Exception();
2000 }
2001 matched = !match->IsNull();
2002 } while (matched);
2003
2004 if (prev < length) {
2005 builder.AddSubjectSlice(prev, length);
2006 }
2007
2008 return *(builder.ToString());
2009}
2010
2011
2012static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2013 ASSERT(args.length() == 4);
2014
2015 CONVERT_CHECKED(String, subject, args[0]);
2016 if (!subject->IsFlat()) {
2017 Object* flat_subject = subject->TryFlatten();
2018 if (flat_subject->IsFailure()) {
2019 return flat_subject;
2020 }
2021 subject = String::cast(flat_subject);
2022 }
2023
2024 CONVERT_CHECKED(String, replacement, args[2]);
2025 if (!replacement->IsFlat()) {
2026 Object* flat_replacement = replacement->TryFlatten();
2027 if (flat_replacement->IsFailure()) {
2028 return flat_replacement;
2029 }
2030 replacement = String::cast(flat_replacement);
2031 }
2032
2033 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2034 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2035
2036 ASSERT(last_match_info->HasFastElements());
2037
2038 return StringReplaceRegExpWithString(subject,
2039 regexp,
2040 replacement,
2041 last_match_info);
2042}
2043
2044
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002045
ager@chromium.org7c537e22008-10-16 08:43:32 +00002046// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2047// limit, we can fix the size of tables.
2048static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002049// Reduce alphabet to this size.
2050static const int kBMAlphabetSize = 0x100;
2051// For patterns below this length, the skip length of Boyer-Moore is too short
2052// to compensate for the algorithmic overhead compared to simple brute force.
2053static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002054
ager@chromium.org7c537e22008-10-16 08:43:32 +00002055// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2056// shift. Only allows the last kBMMaxShift characters of the needle
2057// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002058class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002059 public:
2060 BMGoodSuffixBuffers() {}
2061 inline void init(int needle_length) {
2062 ASSERT(needle_length > 1);
2063 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2064 int len = needle_length - start;
2065 biased_suffixes_ = suffixes_ - start;
2066 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2067 for (int i = 0; i <= len; i++) {
2068 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002069 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002070 }
2071 inline int& suffix(int index) {
2072 ASSERT(biased_suffixes_ + index >= suffixes_);
2073 return biased_suffixes_[index];
2074 }
2075 inline int& shift(int index) {
2076 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2077 return biased_good_suffix_shift_[index];
2078 }
2079 private:
2080 int suffixes_[kBMMaxShift + 1];
2081 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002082 int* biased_suffixes_;
2083 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002084 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2085};
2086
2087// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002088static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002089static BMGoodSuffixBuffers bmgs_buffers;
2090
2091// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002092template <typename pchar>
2093static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
2094 int start) {
2095 // Run forwards to populate bad_char_table, so that *last* instance
2096 // of character equivalence class is the one registered.
2097 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002098 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2099 : kBMAlphabetSize;
2100 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002101 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002102 } else {
2103 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002104 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002105 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002106 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002107 for (int i = start; i < pattern.length() - 1; i++) {
2108 pchar c = pattern[i];
2109 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002110 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002111 }
2112}
2113
2114template <typename pchar>
2115static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002116 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002117 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002118 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002119 // Compute Good Suffix tables.
2120 bmgs_buffers.init(m);
2121
2122 bmgs_buffers.shift(m-1) = 1;
2123 bmgs_buffers.suffix(m) = m + 1;
2124 pchar last_char = pattern[m - 1];
2125 int suffix = m + 1;
2126 for (int i = m; i > start;) {
2127 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2128 if (bmgs_buffers.shift(suffix) == len) {
2129 bmgs_buffers.shift(suffix) = suffix - i;
2130 }
2131 suffix = bmgs_buffers.suffix(suffix);
2132 }
2133 i--;
2134 suffix--;
2135 bmgs_buffers.suffix(i) = suffix;
2136 if (suffix == m) {
2137 // No suffix to extend, so we check against last_char only.
2138 while (i > start && pattern[i - 1] != last_char) {
2139 if (bmgs_buffers.shift(m) == len) {
2140 bmgs_buffers.shift(m) = m - i;
2141 }
2142 i--;
2143 bmgs_buffers.suffix(i) = m;
2144 }
2145 if (i > start) {
2146 i--;
2147 suffix--;
2148 bmgs_buffers.suffix(i) = suffix;
2149 }
2150 }
2151 }
2152 if (suffix < m) {
2153 for (int i = start; i <= m; i++) {
2154 if (bmgs_buffers.shift(i) == len) {
2155 bmgs_buffers.shift(i) = suffix - start;
2156 }
2157 if (i == suffix) {
2158 suffix = bmgs_buffers.suffix(suffix);
2159 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002160 }
2161 }
2162}
2163
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002164template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002165static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002166 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002167 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002168 }
2169 if (sizeof(pchar) == 1) {
2170 if (char_code > String::kMaxAsciiCharCode) {
2171 return -1;
2172 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002173 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002174 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002175 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002176}
2177
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002178// Restricted simplified Boyer-Moore string matching.
2179// Uses only the bad-shift table of Boyer-Moore and only uses it
2180// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002181template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002182static int BoyerMooreHorspool(Vector<const schar> subject,
2183 Vector<const pchar> pattern,
2184 int start_index,
2185 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002186 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002187 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002188 // Only preprocess at most kBMMaxShift last characters of pattern.
2189 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002190
ager@chromium.org7c537e22008-10-16 08:43:32 +00002191 BoyerMoorePopulateBadCharTable(pattern, start);
2192
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002193 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002194 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002195 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002196 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002197 // Perform search
2198 for (idx = start_index; idx <= n - m;) {
2199 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002200 int c;
2201 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002202 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002203 int shift = j - bc_occ;
2204 idx += shift;
2205 badness += 1 - shift; // at most zero, so badness cannot increase.
2206 if (idx > n - m) {
2207 *complete = true;
2208 return -1;
2209 }
2210 }
2211 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002212 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002213 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002214 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002215 return idx;
2216 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002217 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002218 // Badness increases by the number of characters we have
2219 // checked, and decreases by the number of characters we
2220 // can skip by shifting. It's a measure of how we are doing
2221 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002222 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002223 if (badness > 0) {
2224 *complete = false;
2225 return idx;
2226 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002227 }
2228 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002229 *complete = true;
2230 return -1;
2231}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002232
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002233
2234template <typename schar, typename pchar>
2235static int BoyerMooreIndexOf(Vector<const schar> subject,
2236 Vector<const pchar> pattern,
2237 int idx) {
2238 int n = subject.length();
2239 int m = pattern.length();
2240 // Only preprocess at most kBMMaxShift last characters of pattern.
2241 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2242
2243 // Build the Good Suffix table and continue searching.
2244 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2245 pchar last_char = pattern[m - 1];
2246 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002247 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002248 int j = m - 1;
2249 schar c;
2250 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002251 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002252 idx += shift;
2253 if (idx > n - m) {
2254 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002255 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002256 }
2257 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2258 if (j < 0) {
2259 return idx;
2260 } else if (j < start) {
2261 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002262 // Fall back on BMH shift.
2263 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002264 } else {
2265 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002266 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002267 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002268 if (gs_shift > shift) {
2269 shift = gs_shift;
2270 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002271 idx += shift;
2272 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002273 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002274
2275 return -1;
2276}
2277
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002278
2279template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002280static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002281 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002282 int start_index) {
2283 for (int i = start_index, n = string.length(); i < n; i++) {
2284 if (pattern_char == string[i]) {
2285 return i;
2286 }
2287 }
2288 return -1;
2289}
2290
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002291
2292template <typename schar>
2293static int SingleCharLastIndexOf(Vector<const schar> string,
2294 schar pattern_char,
2295 int start_index) {
2296 for (int i = start_index; i >= 0; i--) {
2297 if (pattern_char == string[i]) {
2298 return i;
2299 }
2300 }
2301 return -1;
2302}
2303
2304
ager@chromium.org7c537e22008-10-16 08:43:32 +00002305// Trivial string search for shorter strings.
2306// On return, if "complete" is set to true, the return value is the
2307// final result of searching for the patter in the subject.
2308// If "complete" is set to false, the return value is the index where
2309// further checking should start, i.e., it's guaranteed that the pattern
2310// does not occur at a position prior to the returned index.
2311template <typename pchar, typename schar>
2312static int SimpleIndexOf(Vector<const schar> subject,
2313 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002314 int idx,
2315 bool* complete) {
2316 // Badness is a count of how much work we have done. When we have
2317 // done enough work we decide it's probably worth switching to a better
2318 // algorithm.
2319 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002320 // We know our pattern is at least 2 characters, we cache the first so
2321 // the common case of the first character not matching is faster.
2322 pchar pattern_first_char = pattern[0];
2323
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002324 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2325 badness++;
2326 if (badness > 0) {
2327 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002328 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002329 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002330 if (subject[i] != pattern_first_char) continue;
2331 int j = 1;
2332 do {
2333 if (pattern[j] != subject[i+j]) {
2334 break;
2335 }
2336 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002337 } while (j < pattern.length());
2338 if (j == pattern.length()) {
2339 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002340 return i;
2341 }
2342 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002343 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002344 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002345 return -1;
2346}
2347
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002348// Simple indexOf that never bails out. For short patterns only.
2349template <typename pchar, typename schar>
2350static int SimpleIndexOf(Vector<const schar> subject,
2351 Vector<const pchar> pattern,
2352 int idx) {
2353 pchar pattern_first_char = pattern[0];
2354 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2355 if (subject[i] != pattern_first_char) continue;
2356 int j = 1;
2357 do {
2358 if (pattern[j] != subject[i+j]) {
2359 break;
2360 }
2361 j++;
2362 } while (j < pattern.length());
2363 if (j == pattern.length()) {
2364 return i;
2365 }
2366 }
2367 return -1;
2368}
2369
2370
2371// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002372template <typename schar, typename pchar>
2373static int StringMatchStrategy(Vector<const schar> sub,
2374 Vector<const pchar> pat,
2375 int start_index) {
2376 ASSERT(pat.length() > 1);
2377
2378 // We have an ASCII haystack and a non-ASCII needle. Check if there
2379 // really is a non-ASCII character in the needle and bail out if there
2380 // is.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002381 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002382 for (int i = 0; i < pat.length(); i++) {
2383 uc16 c = pat[i];
2384 if (c > String::kMaxAsciiCharCode) {
2385 return -1;
2386 }
2387 }
2388 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002389 if (pat.length() < kBMMinPatternLength) {
2390 // We don't believe fancy searching can ever be more efficient.
2391 // The max shift of Boyer-Moore on a pattern of this length does
2392 // not compensate for the overhead.
2393 return SimpleIndexOf(sub, pat, start_index);
2394 }
2395 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002396 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002397 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2398 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002399 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002400 if (complete) return idx;
2401 return BoyerMooreIndexOf(sub, pat, idx);
2402}
2403
2404// Perform string match of pattern on subject, starting at start index.
2405// Caller must ensure that 0 <= start_index <= sub->length(),
2406// and should check that pat->length() + start_index <= sub->length()
2407int Runtime::StringMatch(Handle<String> sub,
2408 Handle<String> pat,
2409 int start_index) {
2410 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002411 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002412
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002413 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002414 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002415
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002416 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002417 if (start_index + pattern_length > subject_length) return -1;
2418
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002419 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002420 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002421 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002422 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002423 // character patterns linear search is necessary, so any smart
2424 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002425 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002426 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002427 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002428 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002429 if (pchar > String::kMaxAsciiCharCode) {
2430 return -1;
2431 }
2432 Vector<const char> ascii_vector =
2433 sub->ToAsciiVector().SubVector(start_index, subject_length);
2434 const void* pos = memchr(ascii_vector.start(),
2435 static_cast<const char>(pchar),
2436 static_cast<size_t>(ascii_vector.length()));
2437 if (pos == NULL) {
2438 return -1;
2439 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002440 return static_cast<int>(reinterpret_cast<const char*>(pos)
2441 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002442 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002443 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002444 }
2445
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002446 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002447 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002448 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002449
ager@chromium.org7c537e22008-10-16 08:43:32 +00002450 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2451 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002452 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002453 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002454 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002455 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002456 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002457 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002458 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002459 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002460 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002461 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002462 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002463 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002464}
2465
2466
2467static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002468 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002469 ASSERT(args.length() == 3);
2470
ager@chromium.org7c537e22008-10-16 08:43:32 +00002471 CONVERT_ARG_CHECKED(String, sub, 0);
2472 CONVERT_ARG_CHECKED(String, pat, 1);
2473
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002474 Object* index = args[2];
2475 uint32_t start_index;
2476 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2477
ager@chromium.org870a0b62008-11-04 11:43:05 +00002478 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002479 int position = Runtime::StringMatch(sub, pat, start_index);
2480 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002481}
2482
2483
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002484template <typename schar, typename pchar>
2485static int StringMatchBackwards(Vector<const schar> sub,
2486 Vector<const pchar> pat,
2487 int idx) {
2488 ASSERT(pat.length() >= 1);
2489 ASSERT(idx + pat.length() <= sub.length());
2490
2491 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2492 for (int i = 0; i < pat.length(); i++) {
2493 uc16 c = pat[i];
2494 if (c > String::kMaxAsciiCharCode) {
2495 return -1;
2496 }
2497 }
2498 }
2499
2500 pchar pattern_first_char = pat[0];
2501 for (int i = idx; i >= 0; i--) {
2502 if (sub[i] != pattern_first_char) continue;
2503 int j = 1;
2504 while (j < pat.length()) {
2505 if (pat[j] != sub[i+j]) {
2506 break;
2507 }
2508 j++;
2509 }
2510 if (j == pat.length()) {
2511 return i;
2512 }
2513 }
2514 return -1;
2515}
2516
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002517static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002518 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002519 ASSERT(args.length() == 3);
2520
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002521 CONVERT_ARG_CHECKED(String, sub, 0);
2522 CONVERT_ARG_CHECKED(String, pat, 1);
2523
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002525 uint32_t start_index;
2526 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2527
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002528 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002529 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002530
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002531 if (start_index + pat_length > sub_length) {
2532 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002533 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002534
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002535 if (pat_length == 0) {
2536 return Smi::FromInt(start_index);
2537 }
2538
2539 if (!sub->IsFlat()) {
2540 FlattenString(sub);
2541 }
2542
2543 if (pat_length == 1) {
2544 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2545 if (sub->IsAsciiRepresentation()) {
2546 uc16 pchar = pat->Get(0);
2547 if (pchar > String::kMaxAsciiCharCode) {
2548 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002550 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2551 static_cast<char>(pat->Get(0)),
2552 start_index));
2553 } else {
2554 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2555 pat->Get(0),
2556 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002558 }
2559
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002560 if (!pat->IsFlat()) {
2561 FlattenString(pat);
2562 }
2563
2564 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2565
2566 int position = -1;
2567
2568 if (pat->IsAsciiRepresentation()) {
2569 Vector<const char> pat_vector = pat->ToAsciiVector();
2570 if (sub->IsAsciiRepresentation()) {
2571 position = StringMatchBackwards(sub->ToAsciiVector(),
2572 pat_vector,
2573 start_index);
2574 } else {
2575 position = StringMatchBackwards(sub->ToUC16Vector(),
2576 pat_vector,
2577 start_index);
2578 }
2579 } else {
2580 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2581 if (sub->IsAsciiRepresentation()) {
2582 position = StringMatchBackwards(sub->ToAsciiVector(),
2583 pat_vector,
2584 start_index);
2585 } else {
2586 position = StringMatchBackwards(sub->ToUC16Vector(),
2587 pat_vector,
2588 start_index);
2589 }
2590 }
2591
2592 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002593}
2594
2595
2596static Object* Runtime_StringLocaleCompare(Arguments args) {
2597 NoHandleAllocation ha;
2598 ASSERT(args.length() == 2);
2599
2600 CONVERT_CHECKED(String, str1, args[0]);
2601 CONVERT_CHECKED(String, str2, args[1]);
2602
2603 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002604 int str1_length = str1->length();
2605 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002606
2607 // Decide trivial cases without flattening.
2608 if (str1_length == 0) {
2609 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2610 return Smi::FromInt(-str2_length);
2611 } else {
2612 if (str2_length == 0) return Smi::FromInt(str1_length);
2613 }
2614
2615 int end = str1_length < str2_length ? str1_length : str2_length;
2616
2617 // No need to flatten if we are going to find the answer on the first
2618 // character. At this point we know there is at least one character
2619 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002620 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002621 if (d != 0) return Smi::FromInt(d);
2622
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002623 str1->TryFlatten();
2624 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002625
2626 static StringInputBuffer buf1;
2627 static StringInputBuffer buf2;
2628
2629 buf1.Reset(str1);
2630 buf2.Reset(str2);
2631
2632 for (int i = 0; i < end; i++) {
2633 uint16_t char1 = buf1.GetNext();
2634 uint16_t char2 = buf2.GetNext();
2635 if (char1 != char2) return Smi::FromInt(char1 - char2);
2636 }
2637
2638 return Smi::FromInt(str1_length - str2_length);
2639}
2640
2641
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002642static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643 NoHandleAllocation ha;
2644 ASSERT(args.length() == 3);
2645
2646 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002647 Object* from = args[1];
2648 Object* to = args[2];
2649 int start, end;
2650 // We have a fast integer-only case here to avoid a conversion to double in
2651 // the common case where from and to are Smis.
2652 if (from->IsSmi() && to->IsSmi()) {
2653 start = Smi::cast(from)->value();
2654 end = Smi::cast(to)->value();
2655 } else {
2656 CONVERT_DOUBLE_CHECKED(from_number, from);
2657 CONVERT_DOUBLE_CHECKED(to_number, to);
2658 start = FastD2I(from_number);
2659 end = FastD2I(to_number);
2660 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661 RUNTIME_ASSERT(end >= start);
2662 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002663 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002664 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002665 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002666}
2667
2668
ager@chromium.org41826e72009-03-30 13:30:57 +00002669static Object* Runtime_StringMatch(Arguments args) {
2670 ASSERT_EQ(3, args.length());
2671
2672 CONVERT_ARG_CHECKED(String, subject, 0);
2673 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2674 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2675 HandleScope handles;
2676
2677 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2678
2679 if (match.is_null()) {
2680 return Failure::Exception();
2681 }
2682 if (match->IsNull()) {
2683 return Heap::null_value();
2684 }
2685 int length = subject->length();
2686
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002687 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002688 ZoneList<int> offsets(8);
2689 do {
2690 int start;
2691 int end;
2692 {
2693 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002694 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002695 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2696 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2697 }
2698 offsets.Add(start);
2699 offsets.Add(end);
2700 int index = start < end ? end : end + 1;
2701 if (index > length) break;
2702 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2703 if (match.is_null()) {
2704 return Failure::Exception();
2705 }
2706 } while (!match->IsNull());
2707 int matches = offsets.length() / 2;
2708 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2709 for (int i = 0; i < matches ; i++) {
2710 int from = offsets.at(i * 2);
2711 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002712 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002713 }
2714 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2715 result->set_length(Smi::FromInt(matches));
2716 return *result;
2717}
2718
2719
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002720static Object* Runtime_NumberToRadixString(Arguments args) {
2721 NoHandleAllocation ha;
2722 ASSERT(args.length() == 2);
2723
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002724 // Fast case where the result is a one character string.
2725 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2726 int value = Smi::cast(args[0])->value();
2727 int radix = Smi::cast(args[1])->value();
2728 if (value >= 0 && value < radix) {
2729 RUNTIME_ASSERT(radix <= 36);
2730 // Character array used for conversion.
2731 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2732 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2733 }
2734 }
2735
2736 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002737 CONVERT_DOUBLE_CHECKED(value, args[0]);
2738 if (isnan(value)) {
2739 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2740 }
2741 if (isinf(value)) {
2742 if (value < 0) {
2743 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2744 }
2745 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2746 }
2747 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2748 int radix = FastD2I(radix_number);
2749 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2750 char* str = DoubleToRadixCString(value, radix);
2751 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2752 DeleteArray(str);
2753 return result;
2754}
2755
2756
2757static Object* Runtime_NumberToFixed(Arguments args) {
2758 NoHandleAllocation ha;
2759 ASSERT(args.length() == 2);
2760
2761 CONVERT_DOUBLE_CHECKED(value, args[0]);
2762 if (isnan(value)) {
2763 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2764 }
2765 if (isinf(value)) {
2766 if (value < 0) {
2767 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2768 }
2769 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2770 }
2771 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2772 int f = FastD2I(f_number);
2773 RUNTIME_ASSERT(f >= 0);
2774 char* str = DoubleToFixedCString(value, f);
2775 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2776 DeleteArray(str);
2777 return res;
2778}
2779
2780
2781static Object* Runtime_NumberToExponential(Arguments args) {
2782 NoHandleAllocation ha;
2783 ASSERT(args.length() == 2);
2784
2785 CONVERT_DOUBLE_CHECKED(value, args[0]);
2786 if (isnan(value)) {
2787 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2788 }
2789 if (isinf(value)) {
2790 if (value < 0) {
2791 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2792 }
2793 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2794 }
2795 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2796 int f = FastD2I(f_number);
2797 RUNTIME_ASSERT(f >= -1 && f <= 20);
2798 char* str = DoubleToExponentialCString(value, f);
2799 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2800 DeleteArray(str);
2801 return res;
2802}
2803
2804
2805static Object* Runtime_NumberToPrecision(Arguments args) {
2806 NoHandleAllocation ha;
2807 ASSERT(args.length() == 2);
2808
2809 CONVERT_DOUBLE_CHECKED(value, args[0]);
2810 if (isnan(value)) {
2811 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2812 }
2813 if (isinf(value)) {
2814 if (value < 0) {
2815 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2816 }
2817 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2818 }
2819 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2820 int f = FastD2I(f_number);
2821 RUNTIME_ASSERT(f >= 1 && f <= 21);
2822 char* str = DoubleToPrecisionCString(value, f);
2823 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2824 DeleteArray(str);
2825 return res;
2826}
2827
2828
2829// Returns a single character string where first character equals
2830// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002831static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002832 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002833 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002834 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002835 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002836 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002837 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002838}
2839
2840
2841Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2842 // Handle [] indexing on Strings
2843 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002844 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2845 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002846 }
2847
2848 // Handle [] indexing on String objects
2849 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002850 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2851 Handle<Object> result =
2852 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2853 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002854 }
2855
2856 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002857 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002858 return prototype->GetElement(index);
2859 }
2860
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002861 return GetElement(object, index);
2862}
2863
2864
2865Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002866 return object->GetElement(index);
2867}
2868
2869
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002870Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2871 HandleScope scope;
2872
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002873 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002874 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875 Handle<Object> error =
2876 Factory::NewTypeError("non_object_property_load",
2877 HandleVector(args, 2));
2878 return Top::Throw(*error);
2879 }
2880
2881 // Check if the given key is an array index.
2882 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002883 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002884 return GetElementOrCharAt(object, index);
2885 }
2886
2887 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002888 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002889 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002890 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002891 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002892 bool has_pending_exception = false;
2893 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002894 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002895 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002896 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002897 }
2898
ager@chromium.org32912102009-01-16 10:38:43 +00002899 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002900 // the element if so.
2901 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002902 return GetElementOrCharAt(object, index);
2903 } else {
2904 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002905 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002906 }
2907}
2908
2909
2910static Object* Runtime_GetProperty(Arguments args) {
2911 NoHandleAllocation ha;
2912 ASSERT(args.length() == 2);
2913
2914 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002915 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002916
2917 return Runtime::GetObjectProperty(object, key);
2918}
2919
2920
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002921// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002922static Object* Runtime_KeyedGetProperty(Arguments args) {
2923 NoHandleAllocation ha;
2924 ASSERT(args.length() == 2);
2925
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002926 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002927 // itself.
2928 //
2929 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002930 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002931 // global proxy object never has properties. This is the case
2932 // because the global proxy object forwards everything to its hidden
2933 // prototype including local lookups.
2934 //
2935 // Additionally, we need to make sure that we do not cache results
2936 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002937 if (args[0]->IsJSObject() &&
2938 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002939 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002940 args[1]->IsString()) {
2941 JSObject* receiver = JSObject::cast(args[0]);
2942 String* key = String::cast(args[1]);
2943 if (receiver->HasFastProperties()) {
2944 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002945 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002946 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2947 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002948 Object* value = receiver->FastPropertyAt(offset);
2949 return value->IsTheHole() ? Heap::undefined_value() : value;
2950 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002951 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002952 LookupResult result;
2953 receiver->LocalLookup(key, &result);
2954 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2955 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002956 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002957 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002958 }
2959 } else {
2960 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002961 StringDictionary* dictionary = receiver->property_dictionary();
2962 int entry = dictionary->FindEntry(key);
2963 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002964 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002965 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002966 if (!receiver->IsGlobalObject()) return value;
2967 value = JSGlobalPropertyCell::cast(value)->value();
2968 if (!value->IsTheHole()) return value;
2969 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002970 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002971 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002972 } else if (args[0]->IsString() && args[1]->IsSmi()) {
2973 // Fast case for string indexing using [] with a smi index.
2974 HandleScope scope;
2975 Handle<String> str = args.at<String>(0);
2976 int index = Smi::cast(args[1])->value();
2977 Handle<Object> result = GetCharAt(str, index);
2978 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002979 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002980
2981 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002982 return Runtime::GetObjectProperty(args.at<Object>(0),
2983 args.at<Object>(1));
2984}
2985
2986
ager@chromium.org5c838252010-02-19 08:53:10 +00002987static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
2988 ASSERT(args.length() == 5);
2989 HandleScope scope;
2990 CONVERT_ARG_CHECKED(JSObject, obj, 0);
2991 CONVERT_CHECKED(String, name, args[1]);
2992 CONVERT_CHECKED(Smi, flag_setter, args[2]);
2993 CONVERT_CHECKED(JSFunction, fun, args[3]);
2994 CONVERT_CHECKED(Smi, flag_attr, args[4]);
2995 int unchecked = flag_attr->value();
2996 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2997 RUNTIME_ASSERT(!obj->IsNull());
2998 LookupResult result;
2999 obj->LocalLookupRealNamedProperty(name, &result);
3000
3001 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3002 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3003 // delete it to avoid running into trouble in DefineAccessor, which
3004 // handles this incorrectly if the property is readonly (does nothing)
3005 if (result.IsProperty() &&
3006 (result.type() == FIELD || result.type() == NORMAL
3007 || result.type() == CONSTANT_FUNCTION)) {
3008 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3009 }
3010 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3011}
3012
3013static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3014 ASSERT(args.length() == 4);
3015 HandleScope scope;
3016 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3017 CONVERT_ARG_CHECKED(String, name, 1);
3018 Handle<Object> obj_value = args.at<Object>(2);
3019
3020 CONVERT_CHECKED(Smi, flag, args[3]);
3021 int unchecked = flag->value();
3022 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3023
3024 LookupResult result;
3025 js_object->LocalLookupRealNamedProperty(*name, &result);
3026
3027 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3028
3029 // Take special care when attributes are different and there is already
3030 // a property. For simplicity we normalize the property which enables us
3031 // to not worry about changing the instance_descriptor and creating a new
3032 // map. The current version of SetObjectProperty does not handle attributes
3033 // correctly in the case where a property is a field and is reset with
3034 // new attributes.
3035 if (result.IsProperty() && attr != result.GetAttributes()) {
3036 // New attributes - normalize to avoid writing to instance descriptor
3037 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3038 // Use IgnoreAttributes version since a readonly property may be
3039 // overridden and SetProperty does not allow this.
3040 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3041 *obj_value,
3042 attr);
3043 }
3044 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3045}
3046
3047
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003048Object* Runtime::SetObjectProperty(Handle<Object> object,
3049 Handle<Object> key,
3050 Handle<Object> value,
3051 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003052 HandleScope scope;
3053
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003054 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003055 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003056 Handle<Object> error =
3057 Factory::NewTypeError("non_object_property_store",
3058 HandleVector(args, 2));
3059 return Top::Throw(*error);
3060 }
3061
3062 // If the object isn't a JavaScript object, we ignore the store.
3063 if (!object->IsJSObject()) return *value;
3064
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003065 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3066
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003067 // Check if the given key is an array index.
3068 uint32_t index;
3069 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003070 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3071 // of a string using [] notation. We need to support this too in
3072 // JavaScript.
3073 // In the case of a String object we just need to redirect the assignment to
3074 // the underlying string if the index is in range. Since the underlying
3075 // string does nothing with the assignment then we can ignore such
3076 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003077 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003078 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003079 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003080
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003081 Handle<Object> result = SetElement(js_object, index, value);
3082 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003083 return *value;
3084 }
3085
3086 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003087 Handle<Object> result;
3088 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003089 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003090 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003091 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003092 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003093 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003094 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003095 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003096 return *value;
3097 }
3098
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003099 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003100 bool has_pending_exception = false;
3101 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3102 if (has_pending_exception) return Failure::Exception();
3103 Handle<String> name = Handle<String>::cast(converted);
3104
3105 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003106 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003107 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003108 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003109 }
3110}
3111
3112
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003113Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3114 Handle<Object> key,
3115 Handle<Object> value,
3116 PropertyAttributes attr) {
3117 HandleScope scope;
3118
3119 // Check if the given key is an array index.
3120 uint32_t index;
3121 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003122 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3123 // of a string using [] notation. We need to support this too in
3124 // JavaScript.
3125 // In the case of a String object we just need to redirect the assignment to
3126 // the underlying string if the index is in range. Since the underlying
3127 // string does nothing with the assignment then we can ignore such
3128 // assignments.
3129 if (js_object->IsStringObjectWithCharacterAt(index)) {
3130 return *value;
3131 }
3132
3133 return js_object->SetElement(index, *value);
3134 }
3135
3136 if (key->IsString()) {
3137 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003138 return js_object->SetElement(index, *value);
3139 } else {
3140 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003141 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003142 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3143 *value,
3144 attr);
3145 }
3146 }
3147
3148 // Call-back into JavaScript to convert the key to a string.
3149 bool has_pending_exception = false;
3150 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3151 if (has_pending_exception) return Failure::Exception();
3152 Handle<String> name = Handle<String>::cast(converted);
3153
3154 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003155 return js_object->SetElement(index, *value);
3156 } else {
3157 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3158 }
3159}
3160
3161
ager@chromium.orge2902be2009-06-08 12:21:35 +00003162Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3163 Handle<Object> key) {
3164 HandleScope scope;
3165
3166 // Check if the given key is an array index.
3167 uint32_t index;
3168 if (Array::IndexFromObject(*key, &index)) {
3169 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3170 // characters of a string using [] notation. In the case of a
3171 // String object we just need to redirect the deletion to the
3172 // underlying string if the index is in range. Since the
3173 // underlying string does nothing with the deletion, we can ignore
3174 // such deletions.
3175 if (js_object->IsStringObjectWithCharacterAt(index)) {
3176 return Heap::true_value();
3177 }
3178
3179 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3180 }
3181
3182 Handle<String> key_string;
3183 if (key->IsString()) {
3184 key_string = Handle<String>::cast(key);
3185 } else {
3186 // Call-back into JavaScript to convert the key to a string.
3187 bool has_pending_exception = false;
3188 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3189 if (has_pending_exception) return Failure::Exception();
3190 key_string = Handle<String>::cast(converted);
3191 }
3192
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003193 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003194 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3195}
3196
3197
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003198static Object* Runtime_SetProperty(Arguments args) {
3199 NoHandleAllocation ha;
3200 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3201
3202 Handle<Object> object = args.at<Object>(0);
3203 Handle<Object> key = args.at<Object>(1);
3204 Handle<Object> value = args.at<Object>(2);
3205
3206 // Compute attributes.
3207 PropertyAttributes attributes = NONE;
3208 if (args.length() == 4) {
3209 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003210 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003211 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003212 RUNTIME_ASSERT(
3213 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3214 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003215 }
3216 return Runtime::SetObjectProperty(object, key, value, attributes);
3217}
3218
3219
3220// Set a local property, even if it is READ_ONLY. If the property does not
3221// exist, it will be added with attributes NONE.
3222static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
3223 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003224 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003225 CONVERT_CHECKED(JSObject, object, args[0]);
3226 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003227 // Compute attributes.
3228 PropertyAttributes attributes = NONE;
3229 if (args.length() == 4) {
3230 CONVERT_CHECKED(Smi, value_obj, args[3]);
3231 int unchecked_value = value_obj->value();
3232 // Only attribute bits should be set.
3233 RUNTIME_ASSERT(
3234 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3235 attributes = static_cast<PropertyAttributes>(unchecked_value);
3236 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003237
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003238 return object->
3239 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003240}
3241
3242
3243static Object* Runtime_DeleteProperty(Arguments args) {
3244 NoHandleAllocation ha;
3245 ASSERT(args.length() == 2);
3246
3247 CONVERT_CHECKED(JSObject, object, args[0]);
3248 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00003249 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003250}
3251
3252
ager@chromium.org9085a012009-05-11 19:22:57 +00003253static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
3254 Handle<String> key) {
3255 if (object->HasLocalProperty(*key)) return Heap::true_value();
3256 // Handle hidden prototypes. If there's a hidden prototype above this thing
3257 // then we have to check it for properties, because they are supposed to
3258 // look like they are on this object.
3259 Handle<Object> proto(object->GetPrototype());
3260 if (proto->IsJSObject() &&
3261 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
3262 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
3263 }
3264 return Heap::false_value();
3265}
3266
3267
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003268static Object* Runtime_HasLocalProperty(Arguments args) {
3269 NoHandleAllocation ha;
3270 ASSERT(args.length() == 2);
3271 CONVERT_CHECKED(String, key, args[1]);
3272
ager@chromium.org9085a012009-05-11 19:22:57 +00003273 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003274 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00003275 if (obj->IsJSObject()) {
3276 JSObject* object = JSObject::cast(obj);
3277 // Fast case - no interceptors.
3278 if (object->HasRealNamedProperty(key)) return Heap::true_value();
3279 // Slow case. Either it's not there or we have an interceptor. We should
3280 // have handles for this kind of deal.
3281 HandleScope scope;
3282 return HasLocalPropertyImplementation(Handle<JSObject>(object),
3283 Handle<String>(key));
3284 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003285 // Well, there is one exception: Handle [] on strings.
3286 uint32_t index;
3287 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00003288 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003289 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003290 return Heap::true_value();
3291 }
3292 }
3293 return Heap::false_value();
3294}
3295
3296
3297static Object* Runtime_HasProperty(Arguments args) {
3298 NoHandleAllocation na;
3299 ASSERT(args.length() == 2);
3300
3301 // Only JS objects can have properties.
3302 if (args[0]->IsJSObject()) {
3303 JSObject* object = JSObject::cast(args[0]);
3304 CONVERT_CHECKED(String, key, args[1]);
3305 if (object->HasProperty(key)) return Heap::true_value();
3306 }
3307 return Heap::false_value();
3308}
3309
3310
3311static Object* Runtime_HasElement(Arguments args) {
3312 NoHandleAllocation na;
3313 ASSERT(args.length() == 2);
3314
3315 // Only JS objects can have elements.
3316 if (args[0]->IsJSObject()) {
3317 JSObject* object = JSObject::cast(args[0]);
3318 CONVERT_CHECKED(Smi, index_obj, args[1]);
3319 uint32_t index = index_obj->value();
3320 if (object->HasElement(index)) return Heap::true_value();
3321 }
3322 return Heap::false_value();
3323}
3324
3325
3326static Object* Runtime_IsPropertyEnumerable(Arguments args) {
3327 NoHandleAllocation ha;
3328 ASSERT(args.length() == 2);
3329
3330 CONVERT_CHECKED(JSObject, object, args[0]);
3331 CONVERT_CHECKED(String, key, args[1]);
3332
3333 uint32_t index;
3334 if (key->AsArrayIndex(&index)) {
3335 return Heap::ToBoolean(object->HasElement(index));
3336 }
3337
ager@chromium.org870a0b62008-11-04 11:43:05 +00003338 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
3339 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003340}
3341
3342
3343static Object* Runtime_GetPropertyNames(Arguments args) {
3344 HandleScope scope;
3345 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003346 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003347 return *GetKeysFor(object);
3348}
3349
3350
3351// Returns either a FixedArray as Runtime_GetPropertyNames,
3352// or, if the given object has an enum cache that contains
3353// all enumerable properties of the object and its prototypes
3354// have none, the map of the object. This is used to speed up
3355// the check for deletions during a for-in.
3356static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3357 ASSERT(args.length() == 1);
3358
3359 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3360
3361 if (raw_object->IsSimpleEnum()) return raw_object->map();
3362
3363 HandleScope scope;
3364 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003365 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3366 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003367
3368 // Test again, since cache may have been built by preceding call.
3369 if (object->IsSimpleEnum()) return object->map();
3370
3371 return *content;
3372}
3373
3374
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003375// Find the length of the prototype chain that is to to handled as one. If a
3376// prototype object is hidden it is to be viewed as part of the the object it
3377// is prototype for.
3378static int LocalPrototypeChainLength(JSObject* obj) {
3379 int count = 1;
3380 Object* proto = obj->GetPrototype();
3381 while (proto->IsJSObject() &&
3382 JSObject::cast(proto)->map()->is_hidden_prototype()) {
3383 count++;
3384 proto = JSObject::cast(proto)->GetPrototype();
3385 }
3386 return count;
3387}
3388
3389
3390// Return the names of the local named properties.
3391// args[0]: object
3392static Object* Runtime_GetLocalPropertyNames(Arguments args) {
3393 HandleScope scope;
3394 ASSERT(args.length() == 1);
3395 if (!args[0]->IsJSObject()) {
3396 return Heap::undefined_value();
3397 }
3398 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3399
3400 // Skip the global proxy as it has no properties and always delegates to the
3401 // real global object.
3402 if (obj->IsJSGlobalProxy()) {
3403 // Only collect names if access is permitted.
3404 if (obj->IsAccessCheckNeeded() &&
3405 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
3406 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
3407 return *Factory::NewJSArray(0);
3408 }
3409 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
3410 }
3411
3412 // Find the number of objects making up this.
3413 int length = LocalPrototypeChainLength(*obj);
3414
3415 // Find the number of local properties for each of the objects.
3416 int* local_property_count = NewArray<int>(length);
3417 int total_property_count = 0;
3418 Handle<JSObject> jsproto = obj;
3419 for (int i = 0; i < length; i++) {
3420 // Only collect names if access is permitted.
3421 if (jsproto->IsAccessCheckNeeded() &&
3422 !Top::MayNamedAccess(*jsproto,
3423 Heap::undefined_value(),
3424 v8::ACCESS_KEYS)) {
3425 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
3426 return *Factory::NewJSArray(0);
3427 }
3428 int n;
3429 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
3430 local_property_count[i] = n;
3431 total_property_count += n;
3432 if (i < length - 1) {
3433 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3434 }
3435 }
3436
3437 // Allocate an array with storage for all the property names.
3438 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
3439
3440 // Get the property names.
3441 jsproto = obj;
3442 int proto_with_hidden_properties = 0;
3443 for (int i = 0; i < length; i++) {
3444 jsproto->GetLocalPropertyNames(*names,
3445 i == 0 ? 0 : local_property_count[i - 1]);
3446 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
3447 proto_with_hidden_properties++;
3448 }
3449 if (i < length - 1) {
3450 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3451 }
3452 }
3453
3454 // Filter out name of hidden propeties object.
3455 if (proto_with_hidden_properties > 0) {
3456 Handle<FixedArray> old_names = names;
3457 names = Factory::NewFixedArray(
3458 names->length() - proto_with_hidden_properties);
3459 int dest_pos = 0;
3460 for (int i = 0; i < total_property_count; i++) {
3461 Object* name = old_names->get(i);
3462 if (name == Heap::hidden_symbol()) {
3463 continue;
3464 }
3465 names->set(dest_pos++, name);
3466 }
3467 }
3468
3469 DeleteArray(local_property_count);
3470 return *Factory::NewJSArrayWithElements(names);
3471}
3472
3473
3474// Return the names of the local indexed properties.
3475// args[0]: object
3476static Object* Runtime_GetLocalElementNames(Arguments args) {
3477 HandleScope scope;
3478 ASSERT(args.length() == 1);
3479 if (!args[0]->IsJSObject()) {
3480 return Heap::undefined_value();
3481 }
3482 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3483
3484 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
3485 Handle<FixedArray> names = Factory::NewFixedArray(n);
3486 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
3487 return *Factory::NewJSArrayWithElements(names);
3488}
3489
3490
3491// Return information on whether an object has a named or indexed interceptor.
3492// args[0]: object
3493static Object* Runtime_GetInterceptorInfo(Arguments args) {
3494 HandleScope scope;
3495 ASSERT(args.length() == 1);
3496 if (!args[0]->IsJSObject()) {
3497 return Smi::FromInt(0);
3498 }
3499 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3500
3501 int result = 0;
3502 if (obj->HasNamedInterceptor()) result |= 2;
3503 if (obj->HasIndexedInterceptor()) result |= 1;
3504
3505 return Smi::FromInt(result);
3506}
3507
3508
3509// Return property names from named interceptor.
3510// args[0]: object
3511static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
3512 HandleScope scope;
3513 ASSERT(args.length() == 1);
3514 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3515
3516 if (obj->HasNamedInterceptor()) {
3517 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
3518 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3519 }
3520 return Heap::undefined_value();
3521}
3522
3523
3524// Return element names from indexed interceptor.
3525// args[0]: object
3526static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
3527 HandleScope scope;
3528 ASSERT(args.length() == 1);
3529 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3530
3531 if (obj->HasIndexedInterceptor()) {
3532 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
3533 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3534 }
3535 return Heap::undefined_value();
3536}
3537
3538
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003539static Object* Runtime_LocalKeys(Arguments args) {
3540 ASSERT_EQ(args.length(), 1);
3541 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3542 HandleScope scope;
3543 Handle<JSObject> object(raw_object);
3544 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3545 LOCAL_ONLY);
3546 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3547 // property array and since the result is mutable we have to create
3548 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003549 int length = contents->length();
3550 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3551 for (int i = 0; i < length; i++) {
3552 Object* entry = contents->get(i);
3553 if (entry->IsString()) {
3554 copy->set(i, entry);
3555 } else {
3556 ASSERT(entry->IsNumber());
3557 HandleScope scope;
3558 Handle<Object> entry_handle(entry);
3559 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3560 copy->set(i, *entry_str);
3561 }
3562 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003563 return *Factory::NewJSArrayWithElements(copy);
3564}
3565
3566
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003567static Object* Runtime_GetArgumentsProperty(Arguments args) {
3568 NoHandleAllocation ha;
3569 ASSERT(args.length() == 1);
3570
3571 // Compute the frame holding the arguments.
3572 JavaScriptFrameIterator it;
3573 it.AdvanceToArgumentsFrame();
3574 JavaScriptFrame* frame = it.frame();
3575
3576 // Get the actual number of provided arguments.
3577 const uint32_t n = frame->GetProvidedParametersCount();
3578
3579 // Try to convert the key to an index. If successful and within
3580 // index return the the argument from the frame.
3581 uint32_t index;
3582 if (Array::IndexFromObject(args[0], &index) && index < n) {
3583 return frame->GetParameter(index);
3584 }
3585
3586 // Convert the key to a string.
3587 HandleScope scope;
3588 bool exception = false;
3589 Handle<Object> converted =
3590 Execution::ToString(args.at<Object>(0), &exception);
3591 if (exception) return Failure::Exception();
3592 Handle<String> key = Handle<String>::cast(converted);
3593
3594 // Try to convert the string key into an array index.
3595 if (key->AsArrayIndex(&index)) {
3596 if (index < n) {
3597 return frame->GetParameter(index);
3598 } else {
3599 return Top::initial_object_prototype()->GetElement(index);
3600 }
3601 }
3602
3603 // Handle special arguments properties.
3604 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3605 if (key->Equals(Heap::callee_symbol())) return frame->function();
3606
3607 // Lookup in the initial Object.prototype object.
3608 return Top::initial_object_prototype()->GetProperty(*key);
3609}
3610
3611
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003612static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003613 HandleScope scope;
3614
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003615 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003616 Handle<Object> object = args.at<Object>(0);
3617 if (object->IsJSObject()) {
3618 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00003619 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
3620 js_object->TransformToFastProperties(0);
3621 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003622 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003623 return *object;
3624}
3625
3626
3627static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003628 HandleScope scope;
3629
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003630 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003631 Handle<Object> object = args.at<Object>(0);
3632 if (object->IsJSObject()) {
3633 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003634 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003635 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003636 return *object;
3637}
3638
3639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003640static Object* Runtime_ToBool(Arguments args) {
3641 NoHandleAllocation ha;
3642 ASSERT(args.length() == 1);
3643
3644 return args[0]->ToBoolean();
3645}
3646
3647
3648// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3649// Possible optimizations: put the type string into the oddballs.
3650static Object* Runtime_Typeof(Arguments args) {
3651 NoHandleAllocation ha;
3652
3653 Object* obj = args[0];
3654 if (obj->IsNumber()) return Heap::number_symbol();
3655 HeapObject* heap_obj = HeapObject::cast(obj);
3656
3657 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003658 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003659
3660 InstanceType instance_type = heap_obj->map()->instance_type();
3661 if (instance_type < FIRST_NONSTRING_TYPE) {
3662 return Heap::string_symbol();
3663 }
3664
3665 switch (instance_type) {
3666 case ODDBALL_TYPE:
3667 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3668 return Heap::boolean_symbol();
3669 }
3670 if (heap_obj->IsNull()) {
3671 return Heap::object_symbol();
3672 }
3673 ASSERT(heap_obj->IsUndefined());
3674 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003675 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003676 return Heap::function_symbol();
3677 default:
3678 // For any kind of object not handled above, the spec rule for
3679 // host objects gives that it is okay to return "object"
3680 return Heap::object_symbol();
3681 }
3682}
3683
3684
3685static Object* Runtime_StringToNumber(Arguments args) {
3686 NoHandleAllocation ha;
3687 ASSERT(args.length() == 1);
3688 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003689 subject->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003690 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3691}
3692
3693
3694static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3695 NoHandleAllocation ha;
3696 ASSERT(args.length() == 1);
3697
3698 CONVERT_CHECKED(JSArray, codes, args[0]);
3699 int length = Smi::cast(codes->length())->value();
3700
3701 // Check if the string can be ASCII.
3702 int i;
3703 for (i = 0; i < length; i++) {
3704 Object* element = codes->GetElement(i);
3705 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3706 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3707 break;
3708 }
3709
3710 Object* object = NULL;
3711 if (i == length) { // The string is ASCII.
3712 object = Heap::AllocateRawAsciiString(length);
3713 } else { // The string is not ASCII.
3714 object = Heap::AllocateRawTwoByteString(length);
3715 }
3716
3717 if (object->IsFailure()) return object;
3718 String* result = String::cast(object);
3719 for (int i = 0; i < length; i++) {
3720 Object* element = codes->GetElement(i);
3721 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003722 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 }
3724 return result;
3725}
3726
3727
3728// kNotEscaped is generated by the following:
3729//
3730// #!/bin/perl
3731// for (my $i = 0; $i < 256; $i++) {
3732// print "\n" if $i % 16 == 0;
3733// my $c = chr($i);
3734// my $escaped = 1;
3735// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3736// print $escaped ? "0, " : "1, ";
3737// }
3738
3739
3740static bool IsNotEscaped(uint16_t character) {
3741 // Only for 8 bit characters, the rest are always escaped (in a different way)
3742 ASSERT(character < 256);
3743 static const char kNotEscaped[256] = {
3744 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3745 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3746 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3747 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3748 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3749 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3750 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3751 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3752 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3753 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3754 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3755 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3756 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3757 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3758 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3759 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3760 };
3761 return kNotEscaped[character] != 0;
3762}
3763
3764
3765static Object* Runtime_URIEscape(Arguments args) {
3766 const char hex_chars[] = "0123456789ABCDEF";
3767 NoHandleAllocation ha;
3768 ASSERT(args.length() == 1);
3769 CONVERT_CHECKED(String, source, args[0]);
3770
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003771 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003772
3773 int escaped_length = 0;
3774 int length = source->length();
3775 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003776 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003777 buffer->Reset(source);
3778 while (buffer->has_more()) {
3779 uint16_t character = buffer->GetNext();
3780 if (character >= 256) {
3781 escaped_length += 6;
3782 } else if (IsNotEscaped(character)) {
3783 escaped_length++;
3784 } else {
3785 escaped_length += 3;
3786 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003787 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003788 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003789 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003790 Top::context()->mark_out_of_memory();
3791 return Failure::OutOfMemoryException();
3792 }
3793 }
3794 }
3795 // No length change implies no change. Return original string if no change.
3796 if (escaped_length == length) {
3797 return source;
3798 }
3799 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3800 if (o->IsFailure()) return o;
3801 String* destination = String::cast(o);
3802 int dest_position = 0;
3803
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003804 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003805 buffer->Rewind();
3806 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003807 uint16_t chr = buffer->GetNext();
3808 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003809 destination->Set(dest_position, '%');
3810 destination->Set(dest_position+1, 'u');
3811 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3812 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3813 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3814 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003815 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003816 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003817 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003818 dest_position++;
3819 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003820 destination->Set(dest_position, '%');
3821 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3822 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003823 dest_position += 3;
3824 }
3825 }
3826 return destination;
3827}
3828
3829
3830static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3831 static const signed char kHexValue['g'] = {
3832 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3833 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3834 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3835 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3836 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3837 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3838 -1, 10, 11, 12, 13, 14, 15 };
3839
3840 if (character1 > 'f') return -1;
3841 int hi = kHexValue[character1];
3842 if (hi == -1) return -1;
3843 if (character2 > 'f') return -1;
3844 int lo = kHexValue[character2];
3845 if (lo == -1) return -1;
3846 return (hi << 4) + lo;
3847}
3848
3849
ager@chromium.org870a0b62008-11-04 11:43:05 +00003850static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003851 int i,
3852 int length,
3853 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003854 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003855 int32_t hi = 0;
3856 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003857 if (character == '%' &&
3858 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003859 source->Get(i + 1) == 'u' &&
3860 (hi = TwoDigitHex(source->Get(i + 2),
3861 source->Get(i + 3))) != -1 &&
3862 (lo = TwoDigitHex(source->Get(i + 4),
3863 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003864 *step = 6;
3865 return (hi << 8) + lo;
3866 } else if (character == '%' &&
3867 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003868 (lo = TwoDigitHex(source->Get(i + 1),
3869 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003870 *step = 3;
3871 return lo;
3872 } else {
3873 *step = 1;
3874 return character;
3875 }
3876}
3877
3878
3879static Object* Runtime_URIUnescape(Arguments args) {
3880 NoHandleAllocation ha;
3881 ASSERT(args.length() == 1);
3882 CONVERT_CHECKED(String, source, args[0]);
3883
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003884 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003885
3886 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003887 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003888
3889 int unescaped_length = 0;
3890 for (int i = 0; i < length; unescaped_length++) {
3891 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003892 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003893 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003894 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 i += step;
3896 }
3897
3898 // No length change implies no change. Return original string if no change.
3899 if (unescaped_length == length)
3900 return source;
3901
3902 Object* o = ascii ?
3903 Heap::AllocateRawAsciiString(unescaped_length) :
3904 Heap::AllocateRawTwoByteString(unescaped_length);
3905 if (o->IsFailure()) return o;
3906 String* destination = String::cast(o);
3907
3908 int dest_position = 0;
3909 for (int i = 0; i < length; dest_position++) {
3910 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003911 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003912 i += step;
3913 }
3914 return destination;
3915}
3916
3917
3918static Object* Runtime_StringParseInt(Arguments args) {
3919 NoHandleAllocation ha;
3920
3921 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003922 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003923
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003924 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003926 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927 int i;
3928
3929 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003930 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003931 if (i == len) return Heap::nan_value();
3932
3933 // Compute the sign (default to +).
3934 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003935 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003936 sign = -1;
3937 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003938 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003939 i++;
3940 }
3941
3942 // Compute the radix if 0.
3943 if (radix == 0) {
3944 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003945 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946 radix = 8;
3947 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003948 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003949 if (c == 'x' || c == 'X') {
3950 radix = 16;
3951 i += 2;
3952 }
3953 }
3954 }
3955 } else if (radix == 16) {
3956 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003957 if (i + 1 < len && s->Get(i) == '0') {
3958 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003959 if (c == 'x' || c == 'X') i += 2;
3960 }
3961 }
3962
3963 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3964 double value;
3965 int end_index = StringToInt(s, i, radix, &value);
3966 if (end_index != i) {
3967 return Heap::NumberFromDouble(sign * value);
3968 }
3969 return Heap::nan_value();
3970}
3971
3972
3973static Object* Runtime_StringParseFloat(Arguments args) {
3974 NoHandleAllocation ha;
3975 CONVERT_CHECKED(String, str, args[0]);
3976
3977 // ECMA-262 section 15.1.2.3, empty string is NaN
3978 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3979
3980 // Create a number object from the value.
3981 return Heap::NumberFromDouble(value);
3982}
3983
3984
3985static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3986static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3987
3988
3989template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003990static Object* ConvertCaseHelper(String* s,
3991 int length,
3992 int input_string_length,
3993 unibrow::Mapping<Converter, 128>* mapping) {
3994 // We try this twice, once with the assumption that the result is no longer
3995 // than the input and, if that assumption breaks, again with the exact
3996 // length. This may not be pretty, but it is nicer than what was here before
3997 // and I hereby claim my vaffel-is.
3998 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003999 // Allocate the resulting string.
4000 //
4001 // NOTE: This assumes that the upper/lower case of an ascii
4002 // character is also ascii. This is currently the case, but it
4003 // might break in the future if we implement more context and locale
4004 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004005 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004006 ? Heap::AllocateRawAsciiString(length)
4007 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004008 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004009 String* result = String::cast(o);
4010 bool has_changed_character = false;
4011
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004012 // Convert all characters to upper case, assuming that they will fit
4013 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004014 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004015 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004016 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004017 // We can assume that the string is not empty
4018 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004019 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004020 bool has_next = buffer->has_more();
4021 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004022 int char_length = mapping->get(current, next, chars);
4023 if (char_length == 0) {
4024 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004025 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004026 i++;
4027 } else if (char_length == 1) {
4028 // Common case: converting the letter resulted in one character.
4029 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004030 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031 has_changed_character = true;
4032 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004033 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004034 // We've assumed that the result would be as long as the
4035 // input but here is a character that converts to several
4036 // characters. No matter, we calculate the exact length
4037 // of the result and try the whole thing again.
4038 //
4039 // Note that this leaves room for optimization. We could just
4040 // memcpy what we already have to the result string. Also,
4041 // the result string is the last object allocated we could
4042 // "realloc" it and probably, in the vast majority of cases,
4043 // extend the existing string to be able to hold the full
4044 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004045 int next_length = 0;
4046 if (has_next) {
4047 next_length = mapping->get(next, 0, chars);
4048 if (next_length == 0) next_length = 1;
4049 }
4050 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004051 while (buffer->has_more()) {
4052 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004053 // NOTE: we use 0 as the next character here because, while
4054 // the next character may affect what a character converts to,
4055 // it does not in any case affect the length of what it convert
4056 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004057 int char_length = mapping->get(current, 0, chars);
4058 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004059 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004060 if (current_length > Smi::kMaxValue) {
4061 Top::context()->mark_out_of_memory();
4062 return Failure::OutOfMemoryException();
4063 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004064 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004065 // Try again with the real length.
4066 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004067 } else {
4068 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004069 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004070 i++;
4071 }
4072 has_changed_character = true;
4073 }
4074 current = next;
4075 }
4076 if (has_changed_character) {
4077 return result;
4078 } else {
4079 // If we didn't actually change anything in doing the conversion
4080 // we simple return the result and let the converted string
4081 // become garbage; there is no reason to keep two identical strings
4082 // alive.
4083 return s;
4084 }
4085}
4086
4087
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004088static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4089 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4090 if (s->IsConsString()) {
4091 ASSERT(ConsString::cast(s)->second()->length() == 0);
4092 return SeqAsciiString::cast(ConsString::cast(s)->first());
4093 }
4094 return SeqAsciiString::cast(s);
4095}
4096
4097
4098namespace {
4099
4100struct ToLowerTraits {
4101 typedef unibrow::ToLowercase UnibrowConverter;
4102
4103 static bool ConvertAscii(char* dst, char* src, int length) {
4104 bool changed = false;
4105 for (int i = 0; i < length; ++i) {
4106 char c = src[i];
4107 if ('A' <= c && c <= 'Z') {
4108 c += ('a' - 'A');
4109 changed = true;
4110 }
4111 dst[i] = c;
4112 }
4113 return changed;
4114 }
4115};
4116
4117
4118struct ToUpperTraits {
4119 typedef unibrow::ToUppercase UnibrowConverter;
4120
4121 static bool ConvertAscii(char* dst, char* src, int length) {
4122 bool changed = false;
4123 for (int i = 0; i < length; ++i) {
4124 char c = src[i];
4125 if ('a' <= c && c <= 'z') {
4126 c -= ('a' - 'A');
4127 changed = true;
4128 }
4129 dst[i] = c;
4130 }
4131 return changed;
4132 }
4133};
4134
4135} // namespace
4136
4137
4138template <typename ConvertTraits>
4139static Object* ConvertCase(
4140 Arguments args,
4141 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004142 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004143 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004144 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004145
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004146 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004147 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004148 if (length == 0) return s;
4149
4150 // Simpler handling of ascii strings.
4151 //
4152 // NOTE: This assumes that the upper/lower case of an ascii
4153 // character is also ascii. This is currently the case, but it
4154 // might break in the future if we implement more context and locale
4155 // dependent upper/lower conversions.
4156 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4157 if (seq_ascii != NULL) {
4158 Object* o = Heap::AllocateRawAsciiString(length);
4159 if (o->IsFailure()) return o;
4160 SeqAsciiString* result = SeqAsciiString::cast(o);
4161 bool has_changed_character = ConvertTraits::ConvertAscii(
4162 result->GetChars(), seq_ascii->GetChars(), length);
4163 return has_changed_character ? result : s;
4164 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004165
4166 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4167 if (answer->IsSmi()) {
4168 // Retry with correct length.
4169 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4170 }
4171 return answer; // This may be a failure.
4172}
4173
4174
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004175static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004176 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004177}
4178
4179
4180static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004181 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004182}
4183
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004184
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004185static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
4186 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
4187}
4188
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004189
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004190static Object* Runtime_StringTrim(Arguments args) {
4191 NoHandleAllocation ha;
4192 ASSERT(args.length() == 3);
4193
4194 CONVERT_CHECKED(String, s, args[0]);
4195 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
4196 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
4197
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004198 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004199 int length = s->length();
4200
4201 int left = 0;
4202 if (trimLeft) {
4203 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
4204 left++;
4205 }
4206 }
4207
4208 int right = length;
4209 if (trimRight) {
4210 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
4211 right--;
4212 }
4213 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004214 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004215}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004216
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004217
4218// Copies ascii characters to the given fixed array looking up
4219// one-char strings in the cache. Gives up on the first char that is
4220// not in the cache and fills the remainder with smi zeros. Returns
4221// the length of the successfully copied prefix.
4222static int CopyCachedAsciiCharsToArray(const char* chars,
4223 FixedArray* elements,
4224 int length) {
4225 AssertNoAllocation nogc;
4226 FixedArray* ascii_cache = Heap::single_character_string_cache();
4227 Object* undefined = Heap::undefined_value();
4228 int i;
4229 for (i = 0; i < length; ++i) {
4230 Object* value = ascii_cache->get(chars[i]);
4231 if (value == undefined) break;
4232 ASSERT(!Heap::InNewSpace(value));
4233 elements->set(i, value, SKIP_WRITE_BARRIER);
4234 }
4235 if (i < length) {
4236 ASSERT(Smi::FromInt(0) == 0);
4237 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
4238 }
4239#ifdef DEBUG
4240 for (int j = 0; j < length; ++j) {
4241 Object* element = elements->get(j);
4242 ASSERT(element == Smi::FromInt(0) ||
4243 (element->IsString() && String::cast(element)->LooksValid()));
4244 }
4245#endif
4246 return i;
4247}
4248
4249
4250// Converts a String to JSArray.
4251// For example, "foo" => ["f", "o", "o"].
4252static Object* Runtime_StringToArray(Arguments args) {
4253 HandleScope scope;
4254 ASSERT(args.length() == 1);
4255 CONVERT_ARG_CHECKED(String, s, 0);
4256
4257 s->TryFlatten();
4258 const int length = s->length();
4259
4260 Handle<FixedArray> elements;
4261 if (s->IsFlat() && s->IsAsciiRepresentation()) {
4262 Object* obj = Heap::AllocateUninitializedFixedArray(length);
4263 if (obj->IsFailure()) return obj;
4264 elements = Handle<FixedArray>(FixedArray::cast(obj));
4265
4266 Vector<const char> chars = s->ToAsciiVector();
4267 // Note, this will initialize all elements (not only the prefix)
4268 // to prevent GC from seeing partially initialized array.
4269 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
4270 *elements,
4271 length);
4272
4273 for (int i = num_copied_from_cache; i < length; ++i) {
4274 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
4275 }
4276 } else {
4277 elements = Factory::NewFixedArray(length);
4278 for (int i = 0; i < length; ++i) {
4279 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
4280 }
4281 }
4282
4283#ifdef DEBUG
4284 for (int i = 0; i < length; ++i) {
4285 ASSERT(String::cast(elements->get(i))->length() == 1);
4286 }
4287#endif
4288
4289 return *Factory::NewJSArrayWithElements(elements);
4290}
4291
4292
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00004293bool Runtime::IsUpperCaseChar(uint16_t ch) {
4294 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
4295 int char_length = to_upper_mapping.get(ch, 0, chars);
4296 return char_length == 0;
4297}
4298
4299
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004300static Object* Runtime_NumberToString(Arguments args) {
4301 NoHandleAllocation ha;
4302 ASSERT(args.length() == 1);
4303
4304 Object* number = args[0];
4305 RUNTIME_ASSERT(number->IsNumber());
4306
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004307 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004308}
4309
4310
4311static Object* Runtime_NumberToInteger(Arguments args) {
4312 NoHandleAllocation ha;
4313 ASSERT(args.length() == 1);
4314
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004315 CONVERT_DOUBLE_CHECKED(number, args[0]);
4316
4317 // We do not include 0 so that we don't have to treat +0 / -0 cases.
4318 if (number > 0 && number <= Smi::kMaxValue) {
4319 return Smi::FromInt(static_cast<int>(number));
4320 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004321 return Heap::NumberFromDouble(DoubleToInteger(number));
4322}
4323
4324
4325static Object* Runtime_NumberToJSUint32(Arguments args) {
4326 NoHandleAllocation ha;
4327 ASSERT(args.length() == 1);
4328
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004329 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004330 return Heap::NumberFromUint32(number);
4331}
4332
4333
4334static Object* Runtime_NumberToJSInt32(Arguments args) {
4335 NoHandleAllocation ha;
4336 ASSERT(args.length() == 1);
4337
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004338 CONVERT_DOUBLE_CHECKED(number, args[0]);
4339
4340 // We do not include 0 so that we don't have to treat +0 / -0 cases.
4341 if (number > 0 && number <= Smi::kMaxValue) {
4342 return Smi::FromInt(static_cast<int>(number));
4343 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004344 return Heap::NumberFromInt32(DoubleToInt32(number));
4345}
4346
4347
ager@chromium.org870a0b62008-11-04 11:43:05 +00004348// Converts a Number to a Smi, if possible. Returns NaN if the number is not
4349// a small integer.
4350static Object* Runtime_NumberToSmi(Arguments args) {
4351 NoHandleAllocation ha;
4352 ASSERT(args.length() == 1);
4353
4354 Object* obj = args[0];
4355 if (obj->IsSmi()) {
4356 return obj;
4357 }
4358 if (obj->IsHeapNumber()) {
4359 double value = HeapNumber::cast(obj)->value();
4360 int int_value = FastD2I(value);
4361 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
4362 return Smi::FromInt(int_value);
4363 }
4364 }
4365 return Heap::nan_value();
4366}
4367
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004368
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004369static Object* Runtime_NumberAdd(Arguments args) {
4370 NoHandleAllocation ha;
4371 ASSERT(args.length() == 2);
4372
4373 CONVERT_DOUBLE_CHECKED(x, args[0]);
4374 CONVERT_DOUBLE_CHECKED(y, args[1]);
4375 return Heap::AllocateHeapNumber(x + y);
4376}
4377
4378
4379static Object* Runtime_NumberSub(Arguments args) {
4380 NoHandleAllocation ha;
4381 ASSERT(args.length() == 2);
4382
4383 CONVERT_DOUBLE_CHECKED(x, args[0]);
4384 CONVERT_DOUBLE_CHECKED(y, args[1]);
4385 return Heap::AllocateHeapNumber(x - y);
4386}
4387
4388
4389static Object* Runtime_NumberMul(Arguments args) {
4390 NoHandleAllocation ha;
4391 ASSERT(args.length() == 2);
4392
4393 CONVERT_DOUBLE_CHECKED(x, args[0]);
4394 CONVERT_DOUBLE_CHECKED(y, args[1]);
4395 return Heap::AllocateHeapNumber(x * y);
4396}
4397
4398
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004399static Object* Runtime_NumberUnaryMinus(Arguments args) {
4400 NoHandleAllocation ha;
4401 ASSERT(args.length() == 1);
4402
4403 CONVERT_DOUBLE_CHECKED(x, args[0]);
4404 return Heap::AllocateHeapNumber(-x);
4405}
4406
4407
4408static Object* Runtime_NumberDiv(Arguments args) {
4409 NoHandleAllocation ha;
4410 ASSERT(args.length() == 2);
4411
4412 CONVERT_DOUBLE_CHECKED(x, args[0]);
4413 CONVERT_DOUBLE_CHECKED(y, args[1]);
4414 return Heap::NewNumberFromDouble(x / y);
4415}
4416
4417
4418static Object* Runtime_NumberMod(Arguments args) {
4419 NoHandleAllocation ha;
4420 ASSERT(args.length() == 2);
4421
4422 CONVERT_DOUBLE_CHECKED(x, args[0]);
4423 CONVERT_DOUBLE_CHECKED(y, args[1]);
4424
ager@chromium.org3811b432009-10-28 14:53:37 +00004425 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004426 // NewNumberFromDouble may return a Smi instead of a Number object
4427 return Heap::NewNumberFromDouble(x);
4428}
4429
4430
4431static Object* Runtime_StringAdd(Arguments args) {
4432 NoHandleAllocation ha;
4433 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004434 CONVERT_CHECKED(String, str1, args[0]);
4435 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004436 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004437 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004438}
4439
4440
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004441template<typename sinkchar>
4442static inline void StringBuilderConcatHelper(String* special,
4443 sinkchar* sink,
4444 FixedArray* fixed_array,
4445 int array_length) {
4446 int position = 0;
4447 for (int i = 0; i < array_length; i++) {
4448 Object* element = fixed_array->get(i);
4449 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004450 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004451 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004452 int pos;
4453 int len;
4454 if (encoded_slice > 0) {
4455 // Position and length encoded in one smi.
4456 pos = StringBuilderSubstringPosition::decode(encoded_slice);
4457 len = StringBuilderSubstringLength::decode(encoded_slice);
4458 } else {
4459 // Position and length encoded in two smis.
4460 Object* obj = fixed_array->get(++i);
4461 ASSERT(obj->IsSmi());
4462 pos = Smi::cast(obj)->value();
4463 len = -encoded_slice;
4464 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00004465 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004466 sink + position,
4467 pos,
4468 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004469 position += len;
4470 } else {
4471 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004472 int element_length = string->length();
4473 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004474 position += element_length;
4475 }
4476 }
4477}
4478
4479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004480static Object* Runtime_StringBuilderConcat(Arguments args) {
4481 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004482 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004483 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004484 if (!args[1]->IsSmi()) {
4485 Top::context()->mark_out_of_memory();
4486 return Failure::OutOfMemoryException();
4487 }
4488 int array_length = Smi::cast(args[1])->value();
4489 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004490
4491 // This assumption is used by the slice encoding in one or two smis.
4492 ASSERT(Smi::kMaxValue >= String::kMaxLength);
4493
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004494 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004495 if (!array->HasFastElements()) {
4496 return Top::Throw(Heap::illegal_argument_symbol());
4497 }
4498 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004499 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004500 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004501 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004502
4503 if (array_length == 0) {
4504 return Heap::empty_string();
4505 } else if (array_length == 1) {
4506 Object* first = fixed_array->get(0);
4507 if (first->IsString()) return first;
4508 }
4509
ager@chromium.org5ec48922009-05-05 07:25:34 +00004510 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004511 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004512 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004513 for (int i = 0; i < array_length; i++) {
4514 Object* elt = fixed_array->get(i);
4515 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004516 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004517 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004518 if (len > 0) {
4519 // Position and length encoded in one smi.
4520 int pos = len >> 11;
4521 len &= 0x7ff;
4522 if (pos + len > special_length) {
4523 return Top::Throw(Heap::illegal_argument_symbol());
4524 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004525 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004526 } else {
4527 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004528 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004529 // Get the position and check that it is also a smi.
4530 i++;
4531 if (i >= array_length) {
4532 return Top::Throw(Heap::illegal_argument_symbol());
4533 }
4534 Object* pos = fixed_array->get(i);
4535 if (!pos->IsSmi()) {
4536 return Top::Throw(Heap::illegal_argument_symbol());
4537 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004539 } else if (elt->IsString()) {
4540 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004541 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004542 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004543 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004544 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004545 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004546 } else {
4547 return Top::Throw(Heap::illegal_argument_symbol());
4548 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004549 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004550 Top::context()->mark_out_of_memory();
4551 return Failure::OutOfMemoryException();
4552 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004553 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004554 }
4555
4556 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004557 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004558
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004559 if (ascii) {
4560 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004561 if (object->IsFailure()) return object;
4562 SeqAsciiString* answer = SeqAsciiString::cast(object);
4563 StringBuilderConcatHelper(special,
4564 answer->GetChars(),
4565 fixed_array,
4566 array_length);
4567 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004568 } else {
4569 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004570 if (object->IsFailure()) return object;
4571 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
4572 StringBuilderConcatHelper(special,
4573 answer->GetChars(),
4574 fixed_array,
4575 array_length);
4576 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004577 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004578}
4579
4580
4581static Object* Runtime_NumberOr(Arguments args) {
4582 NoHandleAllocation ha;
4583 ASSERT(args.length() == 2);
4584
4585 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4586 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4587 return Heap::NumberFromInt32(x | y);
4588}
4589
4590
4591static Object* Runtime_NumberAnd(Arguments args) {
4592 NoHandleAllocation ha;
4593 ASSERT(args.length() == 2);
4594
4595 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4596 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4597 return Heap::NumberFromInt32(x & y);
4598}
4599
4600
4601static Object* Runtime_NumberXor(Arguments args) {
4602 NoHandleAllocation ha;
4603 ASSERT(args.length() == 2);
4604
4605 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4606 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4607 return Heap::NumberFromInt32(x ^ y);
4608}
4609
4610
4611static Object* Runtime_NumberNot(Arguments args) {
4612 NoHandleAllocation ha;
4613 ASSERT(args.length() == 1);
4614
4615 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4616 return Heap::NumberFromInt32(~x);
4617}
4618
4619
4620static Object* Runtime_NumberShl(Arguments args) {
4621 NoHandleAllocation ha;
4622 ASSERT(args.length() == 2);
4623
4624 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4625 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4626 return Heap::NumberFromInt32(x << (y & 0x1f));
4627}
4628
4629
4630static Object* Runtime_NumberShr(Arguments args) {
4631 NoHandleAllocation ha;
4632 ASSERT(args.length() == 2);
4633
4634 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
4635 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4636 return Heap::NumberFromUint32(x >> (y & 0x1f));
4637}
4638
4639
4640static Object* Runtime_NumberSar(Arguments args) {
4641 NoHandleAllocation ha;
4642 ASSERT(args.length() == 2);
4643
4644 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4645 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4646 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
4647}
4648
4649
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004650static Object* Runtime_NumberEquals(Arguments args) {
4651 NoHandleAllocation ha;
4652 ASSERT(args.length() == 2);
4653
4654 CONVERT_DOUBLE_CHECKED(x, args[0]);
4655 CONVERT_DOUBLE_CHECKED(y, args[1]);
4656 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
4657 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
4658 if (x == y) return Smi::FromInt(EQUAL);
4659 Object* result;
4660 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
4661 result = Smi::FromInt(EQUAL);
4662 } else {
4663 result = Smi::FromInt(NOT_EQUAL);
4664 }
4665 return result;
4666}
4667
4668
4669static Object* Runtime_StringEquals(Arguments args) {
4670 NoHandleAllocation ha;
4671 ASSERT(args.length() == 2);
4672
4673 CONVERT_CHECKED(String, x, args[0]);
4674 CONVERT_CHECKED(String, y, args[1]);
4675
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004676 bool not_equal = !x->Equals(y);
4677 // This is slightly convoluted because the value that signifies
4678 // equality is 0 and inequality is 1 so we have to negate the result
4679 // from String::Equals.
4680 ASSERT(not_equal == 0 || not_equal == 1);
4681 STATIC_CHECK(EQUAL == 0);
4682 STATIC_CHECK(NOT_EQUAL == 1);
4683 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004684}
4685
4686
4687static Object* Runtime_NumberCompare(Arguments args) {
4688 NoHandleAllocation ha;
4689 ASSERT(args.length() == 3);
4690
4691 CONVERT_DOUBLE_CHECKED(x, args[0]);
4692 CONVERT_DOUBLE_CHECKED(y, args[1]);
4693 if (isnan(x) || isnan(y)) return args[2];
4694 if (x == y) return Smi::FromInt(EQUAL);
4695 if (isless(x, y)) return Smi::FromInt(LESS);
4696 return Smi::FromInt(GREATER);
4697}
4698
4699
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004700// Compare two Smis as if they were converted to strings and then
4701// compared lexicographically.
4702static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4703 NoHandleAllocation ha;
4704 ASSERT(args.length() == 2);
4705
4706 // Arrays for the individual characters of the two Smis. Smis are
4707 // 31 bit integers and 10 decimal digits are therefore enough.
4708 static int x_elms[10];
4709 static int y_elms[10];
4710
4711 // Extract the integer values from the Smis.
4712 CONVERT_CHECKED(Smi, x, args[0]);
4713 CONVERT_CHECKED(Smi, y, args[1]);
4714 int x_value = x->value();
4715 int y_value = y->value();
4716
4717 // If the integers are equal so are the string representations.
4718 if (x_value == y_value) return Smi::FromInt(EQUAL);
4719
4720 // If one of the integers are zero the normal integer order is the
4721 // same as the lexicographic order of the string representations.
4722 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4723
ager@chromium.org32912102009-01-16 10:38:43 +00004724 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004725 // smallest because the char code of '-' is less than the char code
4726 // of any digit. Otherwise, we make both values positive.
4727 if (x_value < 0 || y_value < 0) {
4728 if (y_value >= 0) return Smi::FromInt(LESS);
4729 if (x_value >= 0) return Smi::FromInt(GREATER);
4730 x_value = -x_value;
4731 y_value = -y_value;
4732 }
4733
4734 // Convert the integers to arrays of their decimal digits.
4735 int x_index = 0;
4736 int y_index = 0;
4737 while (x_value > 0) {
4738 x_elms[x_index++] = x_value % 10;
4739 x_value /= 10;
4740 }
4741 while (y_value > 0) {
4742 y_elms[y_index++] = y_value % 10;
4743 y_value /= 10;
4744 }
4745
4746 // Loop through the arrays of decimal digits finding the first place
4747 // where they differ.
4748 while (--x_index >= 0 && --y_index >= 0) {
4749 int diff = x_elms[x_index] - y_elms[y_index];
4750 if (diff != 0) return Smi::FromInt(diff);
4751 }
4752
4753 // If one array is a suffix of the other array, the longest array is
4754 // the representation of the largest of the Smis in the
4755 // lexicographic ordering.
4756 return Smi::FromInt(x_index - y_index);
4757}
4758
4759
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004760static Object* StringInputBufferCompare(String* x, String* y) {
4761 static StringInputBuffer bufx;
4762 static StringInputBuffer bufy;
4763 bufx.Reset(x);
4764 bufy.Reset(y);
4765 while (bufx.has_more() && bufy.has_more()) {
4766 int d = bufx.GetNext() - bufy.GetNext();
4767 if (d < 0) return Smi::FromInt(LESS);
4768 else if (d > 0) return Smi::FromInt(GREATER);
4769 }
4770
4771 // x is (non-trivial) prefix of y:
4772 if (bufy.has_more()) return Smi::FromInt(LESS);
4773 // y is prefix of x:
4774 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4775}
4776
4777
4778static Object* FlatStringCompare(String* x, String* y) {
4779 ASSERT(x->IsFlat());
4780 ASSERT(y->IsFlat());
4781 Object* equal_prefix_result = Smi::FromInt(EQUAL);
4782 int prefix_length = x->length();
4783 if (y->length() < prefix_length) {
4784 prefix_length = y->length();
4785 equal_prefix_result = Smi::FromInt(GREATER);
4786 } else if (y->length() > prefix_length) {
4787 equal_prefix_result = Smi::FromInt(LESS);
4788 }
4789 int r;
4790 if (x->IsAsciiRepresentation()) {
4791 Vector<const char> x_chars = x->ToAsciiVector();
4792 if (y->IsAsciiRepresentation()) {
4793 Vector<const char> y_chars = y->ToAsciiVector();
4794 r = memcmp(x_chars.start(), y_chars.start(), prefix_length);
4795 } else {
4796 Vector<const uc16> y_chars = y->ToUC16Vector();
4797 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
4798 }
4799 } else {
4800 Vector<const uc16> x_chars = x->ToUC16Vector();
4801 if (y->IsAsciiRepresentation()) {
4802 Vector<const char> y_chars = y->ToAsciiVector();
4803 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
4804 } else {
4805 Vector<const uc16> y_chars = y->ToUC16Vector();
4806 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
4807 }
4808 }
4809 Object* result;
4810 if (r == 0) {
4811 result = equal_prefix_result;
4812 } else {
4813 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
4814 }
4815 ASSERT(result == StringInputBufferCompare(x, y));
4816 return result;
4817}
4818
4819
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820static Object* Runtime_StringCompare(Arguments args) {
4821 NoHandleAllocation ha;
4822 ASSERT(args.length() == 2);
4823
4824 CONVERT_CHECKED(String, x, args[0]);
4825 CONVERT_CHECKED(String, y, args[1]);
4826
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004827 Counters::string_compare_runtime.Increment();
4828
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829 // A few fast case tests before we flatten.
4830 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004831 if (y->length() == 0) {
4832 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004833 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004834 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004835 return Smi::FromInt(LESS);
4836 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004837
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004838 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004839 if (d < 0) return Smi::FromInt(LESS);
4840 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004841
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004842 x->TryFlatten();
4843 y->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004844
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004845 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
4846 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004847}
4848
4849
4850static Object* Runtime_Math_abs(Arguments args) {
4851 NoHandleAllocation ha;
4852 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004853 Counters::math_abs.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004854
4855 CONVERT_DOUBLE_CHECKED(x, args[0]);
4856 return Heap::AllocateHeapNumber(fabs(x));
4857}
4858
4859
4860static Object* Runtime_Math_acos(Arguments args) {
4861 NoHandleAllocation ha;
4862 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004863 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004864
4865 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004866 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004867}
4868
4869
4870static Object* Runtime_Math_asin(Arguments args) {
4871 NoHandleAllocation ha;
4872 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004873 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004874
4875 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004876 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004877}
4878
4879
4880static Object* Runtime_Math_atan(Arguments args) {
4881 NoHandleAllocation ha;
4882 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004883 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004884
4885 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004886 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004887}
4888
4889
4890static Object* Runtime_Math_atan2(Arguments args) {
4891 NoHandleAllocation ha;
4892 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004893 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004894
4895 CONVERT_DOUBLE_CHECKED(x, args[0]);
4896 CONVERT_DOUBLE_CHECKED(y, args[1]);
4897 double result;
4898 if (isinf(x) && isinf(y)) {
4899 // Make sure that the result in case of two infinite arguments
4900 // is a multiple of Pi / 4. The sign of the result is determined
4901 // by the first argument (x) and the sign of the second argument
4902 // determines the multiplier: one or three.
4903 static double kPiDividedBy4 = 0.78539816339744830962;
4904 int multiplier = (x < 0) ? -1 : 1;
4905 if (y < 0) multiplier *= 3;
4906 result = multiplier * kPiDividedBy4;
4907 } else {
4908 result = atan2(x, y);
4909 }
4910 return Heap::AllocateHeapNumber(result);
4911}
4912
4913
4914static Object* Runtime_Math_ceil(Arguments args) {
4915 NoHandleAllocation ha;
4916 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004917 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004918
4919 CONVERT_DOUBLE_CHECKED(x, args[0]);
4920 return Heap::NumberFromDouble(ceiling(x));
4921}
4922
4923
4924static Object* Runtime_Math_cos(Arguments args) {
4925 NoHandleAllocation ha;
4926 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004927 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004928
4929 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004930 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004931}
4932
4933
4934static Object* Runtime_Math_exp(Arguments args) {
4935 NoHandleAllocation ha;
4936 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004937 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004938
4939 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004940 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004941}
4942
4943
4944static Object* Runtime_Math_floor(Arguments args) {
4945 NoHandleAllocation ha;
4946 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004947 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004948
4949 CONVERT_DOUBLE_CHECKED(x, args[0]);
4950 return Heap::NumberFromDouble(floor(x));
4951}
4952
4953
4954static Object* Runtime_Math_log(Arguments args) {
4955 NoHandleAllocation ha;
4956 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004957 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004958
4959 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004960 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004961}
4962
4963
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004964// Helper function to compute x^y, where y is known to be an
4965// integer. Uses binary decomposition to limit the number of
4966// multiplications; see the discussion in "Hacker's Delight" by Henry
4967// S. Warren, Jr., figure 11-6, page 213.
4968static double powi(double x, int y) {
4969 ASSERT(y != kMinInt);
4970 unsigned n = (y < 0) ? -y : y;
4971 double m = x;
4972 double p = 1;
4973 while (true) {
4974 if ((n & 1) != 0) p *= m;
4975 n >>= 1;
4976 if (n == 0) {
4977 if (y < 0) {
4978 // Unfortunately, we have to be careful when p has reached
4979 // infinity in the computation, because sometimes the higher
4980 // internal precision in the pow() implementation would have
4981 // given us a finite p. This happens very rarely.
4982 double result = 1.0 / p;
4983 return (result == 0 && isinf(p))
4984 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4985 : result;
4986 } else {
4987 return p;
4988 }
4989 }
4990 m *= m;
4991 }
4992}
4993
4994
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004995static Object* Runtime_Math_pow(Arguments args) {
4996 NoHandleAllocation ha;
4997 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004998 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004999
5000 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005001
5002 // If the second argument is a smi, it is much faster to call the
5003 // custom powi() function than the generic pow().
5004 if (args[1]->IsSmi()) {
5005 int y = Smi::cast(args[1])->value();
5006 return Heap::AllocateHeapNumber(powi(x, y));
5007 }
5008
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005009 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005010
5011 if (!isinf(x)) {
5012 if (y == 0.5) {
5013 // It's not uncommon to use Math.pow(x, 0.5) to compute the
5014 // square root of a number. To speed up such computations, we
5015 // explictly check for this case and use the sqrt() function
5016 // which is faster than pow().
5017 return Heap::AllocateHeapNumber(sqrt(x));
5018 } else if (y == -0.5) {
5019 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
5020 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
5021 }
5022 }
5023
5024 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005025 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005026 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5027 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005028 } else {
5029 return Heap::AllocateHeapNumber(pow(x, y));
5030 }
5031}
5032
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005033// Fast version of Math.pow if we know that y is not an integer and
5034// y is not -0.5 or 0.5. Used as slowcase from codegen.
5035static Object* Runtime_Math_pow_cfunction(Arguments args) {
5036 NoHandleAllocation ha;
5037 ASSERT(args.length() == 2);
5038 CONVERT_DOUBLE_CHECKED(x, args[0]);
5039 CONVERT_DOUBLE_CHECKED(y, args[1]);
5040 if (y == 0) {
5041 return Smi::FromInt(1);
5042 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5043 return Heap::nan_value();
5044 } else {
5045 return Heap::AllocateHeapNumber(pow(x, y));
5046 }
5047}
5048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005049
5050static Object* Runtime_Math_round(Arguments args) {
5051 NoHandleAllocation ha;
5052 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005053 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005054
5055 CONVERT_DOUBLE_CHECKED(x, args[0]);
5056 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005057 double integer = ceil(x);
5058 if (integer - x > 0.5) { integer -= 1.0; }
5059 return Heap::NumberFromDouble(integer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005060}
5061
5062
5063static Object* Runtime_Math_sin(Arguments args) {
5064 NoHandleAllocation ha;
5065 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005066 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005067
5068 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005069 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005070}
5071
5072
5073static Object* Runtime_Math_sqrt(Arguments args) {
5074 NoHandleAllocation ha;
5075 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005076 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005077
5078 CONVERT_DOUBLE_CHECKED(x, args[0]);
5079 return Heap::AllocateHeapNumber(sqrt(x));
5080}
5081
5082
5083static Object* Runtime_Math_tan(Arguments args) {
5084 NoHandleAllocation ha;
5085 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005086 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005087
5088 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005089 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005090}
5091
5092
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005093static Object* Runtime_DateMakeDay(Arguments args) {
5094 NoHandleAllocation ha;
5095 ASSERT(args.length() == 3);
5096
5097 CONVERT_SMI_CHECKED(year, args[0]);
5098 CONVERT_SMI_CHECKED(month, args[1]);
5099 CONVERT_SMI_CHECKED(date, args[2]);
5100
5101 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
5102 181, 212, 243, 273, 304, 334};
5103 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
5104 182, 213, 244, 274, 305, 335};
5105
5106 year += month / 12;
5107 month %= 12;
5108 if (month < 0) {
5109 year--;
5110 month += 12;
5111 }
5112
5113 ASSERT(month >= 0);
5114 ASSERT(month < 12);
5115
5116 // year_delta is an arbitrary number such that:
5117 // a) year_delta = -1 (mod 400)
5118 // b) year + year_delta > 0 for years in the range defined by
5119 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
5120 // Jan 1 1970. This is required so that we don't run into integer
5121 // division of negative numbers.
5122 // c) there shouldn't be overflow for 32-bit integers in the following
5123 // operations.
5124 static const int year_delta = 399999;
5125 static const int base_day = 365 * (1970 + year_delta) +
5126 (1970 + year_delta) / 4 -
5127 (1970 + year_delta) / 100 +
5128 (1970 + year_delta) / 400;
5129
5130 int year1 = year + year_delta;
5131 int day_from_year = 365 * year1 +
5132 year1 / 4 -
5133 year1 / 100 +
5134 year1 / 400 -
5135 base_day;
5136
5137 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
5138 return Smi::FromInt(day_from_year + day_from_month[month] + date - 1);
5139 }
5140
5141 return Smi::FromInt(day_from_year + day_from_month_leap[month] + date - 1);
5142}
5143
5144
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005145static Object* Runtime_NewArgumentsFast(Arguments args) {
5146 NoHandleAllocation ha;
5147 ASSERT(args.length() == 3);
5148
5149 JSFunction* callee = JSFunction::cast(args[0]);
5150 Object** parameters = reinterpret_cast<Object**>(args[1]);
5151 const int length = Smi::cast(args[2])->value();
5152
5153 Object* result = Heap::AllocateArgumentsObject(callee, length);
5154 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005155 // Allocate the elements if needed.
5156 if (length > 0) {
5157 // Allocate the fixed array.
5158 Object* obj = Heap::AllocateRawFixedArray(length);
5159 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005160
5161 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005162 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
5163 FixedArray* array = FixedArray::cast(obj);
5164 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005165
5166 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005167 for (int i = 0; i < length; i++) {
5168 array->set(i, *--parameters, mode);
5169 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005170 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005171 }
5172 return result;
5173}
5174
5175
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005176static Object* Runtime_NewClosure(Arguments args) {
5177 HandleScope scope;
5178 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00005179 CONVERT_ARG_CHECKED(Context, context, 0);
5180 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005181
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005182 PretenureFlag pretenure = (context->global_context() == *context)
5183 ? TENURED // Allocate global closures in old space.
5184 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005185 Handle<JSFunction> result =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005186 Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005187 return *result;
5188}
5189
5190
ager@chromium.org5c838252010-02-19 08:53:10 +00005191static Code* ComputeConstructStub(Handle<JSFunction> function) {
5192 Handle<Object> prototype = Factory::null_value();
5193 if (function->has_instance_prototype()) {
5194 prototype = Handle<Object>(function->instance_prototype());
5195 }
5196 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005197 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00005198 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005199 if (code->IsFailure()) {
5200 return Builtins::builtin(Builtins::JSConstructStubGeneric);
5201 }
5202 return Code::cast(code);
5203 }
5204
ager@chromium.org5c838252010-02-19 08:53:10 +00005205 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005206}
5207
5208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005209static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005210 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005211 ASSERT(args.length() == 1);
5212
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005213 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005214
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005215 // If the constructor isn't a proper function we throw a type error.
5216 if (!constructor->IsJSFunction()) {
5217 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
5218 Handle<Object> type_error =
5219 Factory::NewTypeError("not_constructor", arguments);
5220 return Top::Throw(*type_error);
5221 }
5222
5223 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005224#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005225 // Handle stepping into constructors if step into is active.
5226 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00005227 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005228 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005229#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005230
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005231 if (function->has_initial_map()) {
5232 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005233 // The 'Function' function ignores the receiver object when
5234 // called using 'new' and creates a new JSFunction object that
5235 // is returned. The receiver object is only used for error
5236 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005237 // JSFunction. Factory::NewJSObject() should not be used to
5238 // allocate JSFunctions since it does not properly initialize
5239 // the shared part of the function. Since the receiver is
5240 // ignored anyway, we use the global object as the receiver
5241 // instead of a new JSFunction object. This way, errors are
5242 // reported the same way whether or not 'Function' is called
5243 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005244 return Top::context()->global();
5245 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005246 }
5247
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005248 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005249 Handle<SharedFunctionInfo> shared(function->shared());
5250 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005251
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005252 bool first_allocation = !function->has_initial_map();
5253 Handle<JSObject> result = Factory::NewJSObject(function);
5254 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005255 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00005256 ComputeConstructStub(Handle<JSFunction>(function)));
5257 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005258 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005259
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00005260 Counters::constructed_objects.Increment();
5261 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005262
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005263 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005264}
5265
5266
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005267static Object* Runtime_LazyCompile(Arguments args) {
5268 HandleScope scope;
5269 ASSERT(args.length() == 1);
5270
5271 Handle<JSFunction> function = args.at<JSFunction>(0);
5272#ifdef DEBUG
5273 if (FLAG_trace_lazy) {
5274 PrintF("[lazy: ");
5275 function->shared()->name()->Print();
5276 PrintF("]\n");
5277 }
5278#endif
5279
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005280 // Compile the target function. Here we compile using CompileLazyInLoop in
5281 // order to get the optimized version. This helps code like delta-blue
5282 // that calls performance-critical routines through constructors. A
5283 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
5284 // direct call. Since the in-loop tracking takes place through CallICs
5285 // this means that things called through constructors are never known to
5286 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005287 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005288 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005289 return Failure::Exception();
5290 }
5291
5292 return function->code();
5293}
5294
5295
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005296static Object* Runtime_GetFunctionDelegate(Arguments args) {
5297 HandleScope scope;
5298 ASSERT(args.length() == 1);
5299 RUNTIME_ASSERT(!args[0]->IsJSFunction());
5300 return *Execution::GetFunctionDelegate(args.at<Object>(0));
5301}
5302
5303
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00005304static Object* Runtime_GetConstructorDelegate(Arguments args) {
5305 HandleScope scope;
5306 ASSERT(args.length() == 1);
5307 RUNTIME_ASSERT(!args[0]->IsJSFunction());
5308 return *Execution::GetConstructorDelegate(args.at<Object>(0));
5309}
5310
5311
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005312static Object* Runtime_NewContext(Arguments args) {
5313 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00005314 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005315
kasper.lund7276f142008-07-30 08:49:36 +00005316 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005317 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
5318 Object* result = Heap::AllocateFunctionContext(length, function);
5319 if (result->IsFailure()) return result;
5320
5321 Top::set_context(Context::cast(result));
5322
kasper.lund7276f142008-07-30 08:49:36 +00005323 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005324}
5325
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005326static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005327 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005328 Object* js_object = object;
5329 if (!js_object->IsJSObject()) {
5330 js_object = js_object->ToObject();
5331 if (js_object->IsFailure()) {
5332 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005333 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005334 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005335 Handle<Object> result =
5336 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
5337 return Top::Throw(*result);
5338 }
5339 }
5340
5341 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005342 Heap::AllocateWithContext(Top::context(),
5343 JSObject::cast(js_object),
5344 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005345 if (result->IsFailure()) return result;
5346
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005347 Context* context = Context::cast(result);
5348 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005349
kasper.lund7276f142008-07-30 08:49:36 +00005350 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005351}
5352
5353
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005354static Object* Runtime_PushContext(Arguments args) {
5355 NoHandleAllocation ha;
5356 ASSERT(args.length() == 1);
5357 return PushContextHelper(args[0], false);
5358}
5359
5360
5361static Object* Runtime_PushCatchContext(Arguments args) {
5362 NoHandleAllocation ha;
5363 ASSERT(args.length() == 1);
5364 return PushContextHelper(args[0], true);
5365}
5366
5367
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005368static Object* Runtime_LookupContext(Arguments args) {
5369 HandleScope scope;
5370 ASSERT(args.length() == 2);
5371
5372 CONVERT_ARG_CHECKED(Context, context, 0);
5373 CONVERT_ARG_CHECKED(String, name, 1);
5374
5375 int index;
5376 PropertyAttributes attributes;
5377 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005378 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005379 context->Lookup(name, flags, &index, &attributes);
5380
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005381 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005382 ASSERT(holder->IsJSObject());
5383 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005384 }
5385
5386 // No intermediate context found. Use global object by default.
5387 return Top::context()->global();
5388}
5389
5390
ager@chromium.orga1645e22009-09-09 19:27:10 +00005391// A mechanism to return a pair of Object pointers in registers (if possible).
5392// How this is achieved is calling convention-dependent.
5393// All currently supported x86 compiles uses calling conventions that are cdecl
5394// variants where a 64-bit value is returned in two 32-bit registers
5395// (edx:eax on ia32, r1:r0 on ARM).
5396// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
5397// In Win64 calling convention, a struct of two pointers is returned in memory,
5398// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005399#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005400struct ObjectPair {
5401 Object* x;
5402 Object* y;
5403};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005404
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005405static inline ObjectPair MakePair(Object* x, Object* y) {
5406 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005407 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
5408 // In Win64 they are assigned to a hidden first argument.
5409 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005410}
5411#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005412typedef uint64_t ObjectPair;
5413static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005414 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005415 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005416}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005417#endif
5418
5419
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005420static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
5422 USE(attributes);
5423 return x->IsTheHole() ? Heap::undefined_value() : x;
5424}
5425
5426
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005427static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
5428 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005429 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005430 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005431 JSFunction* context_extension_function =
5432 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005433 // If the holder isn't a context extension object, we just return it
5434 // as the receiver. This allows arguments objects to be used as
5435 // receivers, but only if they are put in the context scope chain
5436 // explicitly via a with-statement.
5437 Object* constructor = holder->map()->constructor();
5438 if (constructor != context_extension_function) return holder;
5439 // Fall back to using the global object as the receiver if the
5440 // property turns out to be a local variable allocated in a context
5441 // extension object - introduced via eval.
5442 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005443}
5444
5445
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005446static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005447 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00005448 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005449
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005450 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005451 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005452 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005453 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005454 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005455
5456 int index;
5457 PropertyAttributes attributes;
5458 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005459 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005460 context->Lookup(name, flags, &index, &attributes);
5461
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005462 // If the index is non-negative, the slot has been found in a local
5463 // variable or a parameter. Read it from the context object or the
5464 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005465 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005466 // If the "property" we were looking for is a local variable or an
5467 // argument in a context, the receiver is the global object; see
5468 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
5469 JSObject* receiver = Top::context()->global()->global_receiver();
5470 Object* value = (holder->IsContext())
5471 ? Context::cast(*holder)->get(index)
5472 : JSObject::cast(*holder)->GetElement(index);
5473 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005474 }
5475
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005476 // If the holder is found, we read the property from it.
5477 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005478 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005479 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005480 JSObject* receiver;
5481 if (object->IsGlobalObject()) {
5482 receiver = GlobalObject::cast(object)->global_receiver();
5483 } else if (context->is_exception_holder(*holder)) {
5484 receiver = Top::context()->global()->global_receiver();
5485 } else {
5486 receiver = ComputeReceiverForNonGlobal(object);
5487 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005488 // No need to unhole the value here. This is taken care of by the
5489 // GetProperty function.
5490 Object* value = object->GetProperty(*name);
5491 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005492 }
5493
5494 if (throw_error) {
5495 // The property doesn't exist - throw exception.
5496 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005497 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 return MakePair(Top::Throw(*reference_error), NULL);
5499 } else {
5500 // The property doesn't exist - return undefined
5501 return MakePair(Heap::undefined_value(), Heap::undefined_value());
5502 }
5503}
5504
5505
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005506static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005507 return LoadContextSlotHelper(args, true);
5508}
5509
5510
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005511static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005512 return LoadContextSlotHelper(args, false);
5513}
5514
5515
5516static Object* Runtime_StoreContextSlot(Arguments args) {
5517 HandleScope scope;
5518 ASSERT(args.length() == 3);
5519
5520 Handle<Object> value(args[0]);
5521 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005522 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005523
5524 int index;
5525 PropertyAttributes attributes;
5526 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005527 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005528 context->Lookup(name, flags, &index, &attributes);
5529
5530 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005531 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005532 // Ignore if read_only variable.
5533 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005534 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005535 }
5536 } else {
5537 ASSERT((attributes & READ_ONLY) == 0);
5538 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005539 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 USE(result);
5541 ASSERT(!result->IsFailure());
5542 }
5543 return *value;
5544 }
5545
5546 // Slow case: The property is not in a FixedArray context.
5547 // It is either in an JSObject extension context or it was not found.
5548 Handle<JSObject> context_ext;
5549
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005550 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005551 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005552 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005553 } else {
5554 // The property was not found. It needs to be stored in the global context.
5555 ASSERT(attributes == ABSENT);
5556 attributes = NONE;
5557 context_ext = Handle<JSObject>(Top::context()->global());
5558 }
5559
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005560 // Set the property, but ignore if read_only variable on the context
5561 // extension object itself.
5562 if ((attributes & READ_ONLY) == 0 ||
5563 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005564 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
5565 if (set.is_null()) {
5566 // Failure::Exception is converted to a null handle in the
5567 // handle-based methods such as SetProperty. We therefore need
5568 // to convert null handles back to exceptions.
5569 ASSERT(Top::has_pending_exception());
5570 return Failure::Exception();
5571 }
5572 }
5573 return *value;
5574}
5575
5576
5577static Object* Runtime_Throw(Arguments args) {
5578 HandleScope scope;
5579 ASSERT(args.length() == 1);
5580
5581 return Top::Throw(args[0]);
5582}
5583
5584
5585static Object* Runtime_ReThrow(Arguments args) {
5586 HandleScope scope;
5587 ASSERT(args.length() == 1);
5588
5589 return Top::ReThrow(args[0]);
5590}
5591
5592
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005593static Object* Runtime_PromoteScheduledException(Arguments args) {
5594 ASSERT_EQ(0, args.length());
5595 return Top::PromoteScheduledException();
5596}
5597
5598
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005599static Object* Runtime_ThrowReferenceError(Arguments args) {
5600 HandleScope scope;
5601 ASSERT(args.length() == 1);
5602
5603 Handle<Object> name(args[0]);
5604 Handle<Object> reference_error =
5605 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5606 return Top::Throw(*reference_error);
5607}
5608
5609
5610static Object* Runtime_StackOverflow(Arguments args) {
5611 NoHandleAllocation na;
5612 return Top::StackOverflow();
5613}
5614
5615
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005616static Object* Runtime_StackGuard(Arguments args) {
5617 ASSERT(args.length() == 1);
5618
5619 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005620 if (StackGuard::IsStackOverflow()) {
5621 return Runtime_StackOverflow(args);
5622 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005623
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005624 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005625}
5626
5627
5628// NOTE: These PrintXXX functions are defined for all builds (not just
5629// DEBUG builds) because we may want to be able to trace function
5630// calls in all modes.
5631static void PrintString(String* str) {
5632 // not uncommon to have empty strings
5633 if (str->length() > 0) {
5634 SmartPointer<char> s =
5635 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
5636 PrintF("%s", *s);
5637 }
5638}
5639
5640
5641static void PrintObject(Object* obj) {
5642 if (obj->IsSmi()) {
5643 PrintF("%d", Smi::cast(obj)->value());
5644 } else if (obj->IsString() || obj->IsSymbol()) {
5645 PrintString(String::cast(obj));
5646 } else if (obj->IsNumber()) {
5647 PrintF("%g", obj->Number());
5648 } else if (obj->IsFailure()) {
5649 PrintF("<failure>");
5650 } else if (obj->IsUndefined()) {
5651 PrintF("<undefined>");
5652 } else if (obj->IsNull()) {
5653 PrintF("<null>");
5654 } else if (obj->IsTrue()) {
5655 PrintF("<true>");
5656 } else if (obj->IsFalse()) {
5657 PrintF("<false>");
5658 } else {
5659 PrintF("%p", obj);
5660 }
5661}
5662
5663
5664static int StackSize() {
5665 int n = 0;
5666 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
5667 return n;
5668}
5669
5670
5671static void PrintTransition(Object* result) {
5672 // indentation
5673 { const int nmax = 80;
5674 int n = StackSize();
5675 if (n <= nmax)
5676 PrintF("%4d:%*s", n, n, "");
5677 else
5678 PrintF("%4d:%*s", n, nmax, "...");
5679 }
5680
5681 if (result == NULL) {
5682 // constructor calls
5683 JavaScriptFrameIterator it;
5684 JavaScriptFrame* frame = it.frame();
5685 if (frame->IsConstructor()) PrintF("new ");
5686 // function name
5687 Object* fun = frame->function();
5688 if (fun->IsJSFunction()) {
5689 PrintObject(JSFunction::cast(fun)->shared()->name());
5690 } else {
5691 PrintObject(fun);
5692 }
5693 // function arguments
5694 // (we are intentionally only printing the actually
5695 // supplied parameters, not all parameters required)
5696 PrintF("(this=");
5697 PrintObject(frame->receiver());
5698 const int length = frame->GetProvidedParametersCount();
5699 for (int i = 0; i < length; i++) {
5700 PrintF(", ");
5701 PrintObject(frame->GetParameter(i));
5702 }
5703 PrintF(") {\n");
5704
5705 } else {
5706 // function result
5707 PrintF("} -> ");
5708 PrintObject(result);
5709 PrintF("\n");
5710 }
5711}
5712
5713
5714static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005715 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005716 NoHandleAllocation ha;
5717 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005718 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005719}
5720
5721
5722static Object* Runtime_TraceExit(Arguments args) {
5723 NoHandleAllocation ha;
5724 PrintTransition(args[0]);
5725 return args[0]; // return TOS
5726}
5727
5728
5729static Object* Runtime_DebugPrint(Arguments args) {
5730 NoHandleAllocation ha;
5731 ASSERT(args.length() == 1);
5732
5733#ifdef DEBUG
5734 if (args[0]->IsString()) {
5735 // If we have a string, assume it's a code "marker"
5736 // and print some interesting cpu debugging info.
5737 JavaScriptFrameIterator it;
5738 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005739 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
5740 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005741 } else {
5742 PrintF("DebugPrint: ");
5743 }
5744 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005745 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005746 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005747 HeapObject::cast(args[0])->map()->Print();
5748 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005749#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005750 // ShortPrint is available in release mode. Print is not.
5751 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005752#endif
5753 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00005754 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005755
5756 return args[0]; // return TOS
5757}
5758
5759
5760static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005761 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005762 NoHandleAllocation ha;
5763 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005764 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005765}
5766
5767
mads.s.ager31e71382008-08-13 09:32:07 +00005768static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005769 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005770 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005771
5772 // According to ECMA-262, section 15.9.1, page 117, the precision of
5773 // the number in a Date object representing a particular instant in
5774 // time is milliseconds. Therefore, we floor the result of getting
5775 // the OS time.
5776 double millis = floor(OS::TimeCurrentMillis());
5777 return Heap::NumberFromDouble(millis);
5778}
5779
5780
5781static Object* Runtime_DateParseString(Arguments args) {
5782 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005783 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005784
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005785 CONVERT_ARG_CHECKED(String, str, 0);
5786 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005787
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005788 CONVERT_ARG_CHECKED(JSArray, output, 1);
5789 RUNTIME_ASSERT(output->HasFastElements());
5790
5791 AssertNoAllocation no_allocation;
5792
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005793 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005794 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5795 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005796 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005797 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005799 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005800 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5801 }
5802
5803 if (result) {
5804 return *output;
5805 } else {
5806 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005807 }
5808}
5809
5810
5811static Object* Runtime_DateLocalTimezone(Arguments args) {
5812 NoHandleAllocation ha;
5813 ASSERT(args.length() == 1);
5814
5815 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005816 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005817 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5818}
5819
5820
5821static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5822 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005823 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005824
5825 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5826}
5827
5828
5829static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5830 NoHandleAllocation ha;
5831 ASSERT(args.length() == 1);
5832
5833 CONVERT_DOUBLE_CHECKED(x, args[0]);
5834 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5835}
5836
5837
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005838static Object* Runtime_NumberIsFinite(Arguments args) {
5839 NoHandleAllocation ha;
5840 ASSERT(args.length() == 1);
5841
5842 CONVERT_DOUBLE_CHECKED(value, args[0]);
5843 Object* result;
5844 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5845 result = Heap::false_value();
5846 } else {
5847 result = Heap::true_value();
5848 }
5849 return result;
5850}
5851
5852
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005853static Object* Runtime_GlobalReceiver(Arguments args) {
5854 ASSERT(args.length() == 1);
5855 Object* global = args[0];
5856 if (!global->IsJSGlobalObject()) return Heap::null_value();
5857 return JSGlobalObject::cast(global)->global_receiver();
5858}
5859
5860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005861static Object* Runtime_CompileString(Arguments args) {
5862 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005863 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005864 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005865 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005866
ager@chromium.org381abbb2009-02-25 13:23:22 +00005867 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005868 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005869 Compiler::ValidationState validate = (is_json->IsTrue())
5870 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005871 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5872 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005873 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005874 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005875 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005876 Handle<JSFunction> fun =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005877 Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005878 return *fun;
5879}
5880
5881
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005882static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
5883 ASSERT(args.length() == 3);
5884 if (!args[0]->IsJSFunction()) {
5885 return MakePair(Top::ThrowIllegalOperation(), NULL);
5886 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005887
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005888 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005889 Handle<JSFunction> callee = args.at<JSFunction>(0);
5890 Handle<Object> receiver; // Will be overwritten.
5891
5892 // Compute the calling context.
5893 Handle<Context> context = Handle<Context>(Top::context());
5894#ifdef DEBUG
5895 // Make sure Top::context() agrees with the old code that traversed
5896 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005897 StackFrameLocator locator;
5898 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005899 ASSERT(Context::cast(frame->context()) == *context);
5900#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005901
5902 // Find where the 'eval' symbol is bound. It is unaliased only if
5903 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005904 int index = -1;
5905 PropertyAttributes attributes = ABSENT;
5906 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005907 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5908 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005909 // Stop search when eval is found or when the global context is
5910 // reached.
5911 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005912 if (context->is_function_context()) {
5913 context = Handle<Context>(Context::cast(context->closure()->context()));
5914 } else {
5915 context = Handle<Context>(context->previous());
5916 }
5917 }
5918
iposva@chromium.org245aa852009-02-10 00:49:54 +00005919 // If eval could not be resolved, it has been deleted and we need to
5920 // throw a reference error.
5921 if (attributes == ABSENT) {
5922 Handle<Object> name = Factory::eval_symbol();
5923 Handle<Object> reference_error =
5924 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005925 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005926 }
5927
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005928 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005929 // 'eval' is not bound in the global context. Just call the function
5930 // with the given arguments. This is not necessarily the global eval.
5931 if (receiver->IsContext()) {
5932 context = Handle<Context>::cast(receiver);
5933 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005934 } else if (receiver->IsJSContextExtensionObject()) {
5935 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005936 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005937 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005938 }
5939
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005940 // 'eval' is bound in the global context, but it may have been overwritten.
5941 // Compare it to the builtin 'GlobalEval' function to make sure.
5942 if (*callee != Top::global_context()->global_eval_fun() ||
5943 !args[1]->IsString()) {
5944 return MakePair(*callee, Top::context()->global()->global_receiver());
5945 }
5946
5947 // Deal with a normal eval call with a string argument. Compile it
5948 // and return the compiled function bound in the local context.
5949 Handle<String> source = args.at<String>(1);
5950 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5951 source,
5952 Handle<Context>(Top::context()),
5953 Top::context()->IsGlobalContext(),
5954 Compiler::DONT_VALIDATE_JSON);
5955 if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
5956 callee = Factory::NewFunctionFromBoilerplate(
5957 boilerplate,
5958 Handle<Context>(Top::context()),
5959 NOT_TENURED);
5960 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005961}
5962
5963
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005964static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5965 // This utility adjusts the property attributes for newly created Function
5966 // object ("new Function(...)") by changing the map.
5967 // All it does is changing the prototype property to enumerable
5968 // as specified in ECMA262, 15.3.5.2.
5969 HandleScope scope;
5970 ASSERT(args.length() == 1);
5971 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5972 ASSERT(func->map()->instance_type() ==
5973 Top::function_instance_map()->instance_type());
5974 ASSERT(func->map()->instance_size() ==
5975 Top::function_instance_map()->instance_size());
5976 func->set_map(*Top::function_instance_map());
5977 return *func;
5978}
5979
5980
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005981// Push an array unto an array of arrays if it is not already in the
5982// array. Returns true if the element was pushed on the stack and
5983// false otherwise.
5984static Object* Runtime_PushIfAbsent(Arguments args) {
5985 ASSERT(args.length() == 2);
5986 CONVERT_CHECKED(JSArray, array, args[0]);
5987 CONVERT_CHECKED(JSArray, element, args[1]);
5988 RUNTIME_ASSERT(array->HasFastElements());
5989 int length = Smi::cast(array->length())->value();
5990 FixedArray* elements = FixedArray::cast(array->elements());
5991 for (int i = 0; i < length; i++) {
5992 if (elements->get(i) == element) return Heap::false_value();
5993 }
5994 Object* obj = array->SetFastElement(length, element);
5995 if (obj->IsFailure()) return obj;
5996 return Heap::true_value();
5997}
5998
5999
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006000/**
6001 * A simple visitor visits every element of Array's.
6002 * The backend storage can be a fixed array for fast elements case,
6003 * or a dictionary for sparse array. Since Dictionary is a subtype
6004 * of FixedArray, the class can be used by both fast and slow cases.
6005 * The second parameter of the constructor, fast_elements, specifies
6006 * whether the storage is a FixedArray or Dictionary.
6007 *
6008 * An index limit is used to deal with the situation that a result array
6009 * length overflows 32-bit non-negative integer.
6010 */
6011class ArrayConcatVisitor {
6012 public:
6013 ArrayConcatVisitor(Handle<FixedArray> storage,
6014 uint32_t index_limit,
6015 bool fast_elements) :
6016 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006017 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006018
6019 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006020 if (i >= index_limit_ - index_offset_) return;
6021 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006022
6023 if (fast_elements_) {
6024 ASSERT(index < static_cast<uint32_t>(storage_->length()));
6025 storage_->set(index, *elm);
6026
6027 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006028 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
6029 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006030 Factory::DictionaryAtNumberPut(dict, index, elm);
6031 if (!result.is_identical_to(dict))
6032 storage_ = result;
6033 }
6034 }
6035
6036 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006037 if (index_limit_ - index_offset_ < delta) {
6038 index_offset_ = index_limit_;
6039 } else {
6040 index_offset_ += delta;
6041 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006042 }
6043
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00006044 Handle<FixedArray> storage() { return storage_; }
6045
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006046 private:
6047 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006048 // Limit on the accepted indices. Elements with indices larger than the
6049 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006050 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006051 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006052 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006053 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006054};
6055
6056
ager@chromium.org3811b432009-10-28 14:53:37 +00006057template<class ExternalArrayClass, class ElementType>
6058static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
6059 bool elements_are_ints,
6060 bool elements_are_guaranteed_smis,
6061 uint32_t range,
6062 ArrayConcatVisitor* visitor) {
6063 Handle<ExternalArrayClass> array(
6064 ExternalArrayClass::cast(receiver->elements()));
6065 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
6066
6067 if (visitor != NULL) {
6068 if (elements_are_ints) {
6069 if (elements_are_guaranteed_smis) {
6070 for (uint32_t j = 0; j < len; j++) {
6071 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
6072 visitor->visit(j, e);
6073 }
6074 } else {
6075 for (uint32_t j = 0; j < len; j++) {
6076 int64_t val = static_cast<int64_t>(array->get(j));
6077 if (Smi::IsValid(static_cast<intptr_t>(val))) {
6078 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
6079 visitor->visit(j, e);
6080 } else {
6081 Handle<Object> e(
6082 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
6083 visitor->visit(j, e);
6084 }
6085 }
6086 }
6087 } else {
6088 for (uint32_t j = 0; j < len; j++) {
6089 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
6090 visitor->visit(j, e);
6091 }
6092 }
6093 }
6094
6095 return len;
6096}
6097
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006098/**
6099 * A helper function that visits elements of a JSObject. Only elements
6100 * whose index between 0 and range (exclusive) are visited.
6101 *
6102 * If the third parameter, visitor, is not NULL, the visitor is called
6103 * with parameters, 'visitor_index_offset + element index' and the element.
6104 *
6105 * It returns the number of visisted elements.
6106 */
6107static uint32_t IterateElements(Handle<JSObject> receiver,
6108 uint32_t range,
6109 ArrayConcatVisitor* visitor) {
6110 uint32_t num_of_elements = 0;
6111
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006112 switch (receiver->GetElementsKind()) {
6113 case JSObject::FAST_ELEMENTS: {
6114 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
6115 uint32_t len = elements->length();
6116 if (range < len) {
6117 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006118 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006119
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006120 for (uint32_t j = 0; j < len; j++) {
6121 Handle<Object> e(elements->get(j));
6122 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006123 num_of_elements++;
6124 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006125 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006126 }
6127 }
6128 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006129 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006130 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006131 case JSObject::PIXEL_ELEMENTS: {
6132 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
6133 uint32_t len = pixels->length();
6134 if (range < len) {
6135 len = range;
6136 }
6137
6138 for (uint32_t j = 0; j < len; j++) {
6139 num_of_elements++;
6140 if (visitor != NULL) {
6141 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
6142 visitor->visit(j, e);
6143 }
6144 }
6145 break;
6146 }
ager@chromium.org3811b432009-10-28 14:53:37 +00006147 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
6148 num_of_elements =
6149 IterateExternalArrayElements<ExternalByteArray, int8_t>(
6150 receiver, true, true, range, visitor);
6151 break;
6152 }
6153 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6154 num_of_elements =
6155 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
6156 receiver, true, true, range, visitor);
6157 break;
6158 }
6159 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
6160 num_of_elements =
6161 IterateExternalArrayElements<ExternalShortArray, int16_t>(
6162 receiver, true, true, range, visitor);
6163 break;
6164 }
6165 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6166 num_of_elements =
6167 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
6168 receiver, true, true, range, visitor);
6169 break;
6170 }
6171 case JSObject::EXTERNAL_INT_ELEMENTS: {
6172 num_of_elements =
6173 IterateExternalArrayElements<ExternalIntArray, int32_t>(
6174 receiver, true, false, range, visitor);
6175 break;
6176 }
6177 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6178 num_of_elements =
6179 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
6180 receiver, true, false, range, visitor);
6181 break;
6182 }
6183 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
6184 num_of_elements =
6185 IterateExternalArrayElements<ExternalFloatArray, float>(
6186 receiver, false, false, range, visitor);
6187 break;
6188 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006189 case JSObject::DICTIONARY_ELEMENTS: {
6190 Handle<NumberDictionary> dict(receiver->element_dictionary());
6191 uint32_t capacity = dict->Capacity();
6192 for (uint32_t j = 0; j < capacity; j++) {
6193 Handle<Object> k(dict->KeyAt(j));
6194 if (dict->IsKey(*k)) {
6195 ASSERT(k->IsNumber());
6196 uint32_t index = static_cast<uint32_t>(k->Number());
6197 if (index < range) {
6198 num_of_elements++;
6199 if (visitor) {
6200 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
6201 }
6202 }
6203 }
6204 }
6205 break;
6206 }
6207 default:
6208 UNREACHABLE();
6209 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006210 }
6211
6212 return num_of_elements;
6213}
6214
6215
6216/**
6217 * A helper function that visits elements of an Array object, and elements
6218 * on its prototypes.
6219 *
6220 * Elements on prototypes are visited first, and only elements whose indices
6221 * less than Array length are visited.
6222 *
6223 * If a ArrayConcatVisitor object is given, the visitor is called with
6224 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006225 *
6226 * The returned number of elements is an upper bound on the actual number
6227 * of elements added. If the same element occurs in more than one object
6228 * in the array's prototype chain, it will be counted more than once, but
6229 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006230 */
6231static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
6232 ArrayConcatVisitor* visitor) {
6233 uint32_t range = static_cast<uint32_t>(array->length()->Number());
6234 Handle<Object> obj = array;
6235
6236 static const int kEstimatedPrototypes = 3;
6237 List< Handle<JSObject> > objects(kEstimatedPrototypes);
6238
6239 // Visit prototype first. If an element on the prototype is shadowed by
6240 // the inheritor using the same index, the ArrayConcatVisitor visits
6241 // the prototype element before the shadowing element.
6242 // The visitor can simply overwrite the old value by new value using
6243 // the same index. This follows Array::concat semantics.
6244 while (!obj->IsNull()) {
6245 objects.Add(Handle<JSObject>::cast(obj));
6246 obj = Handle<Object>(obj->GetPrototype());
6247 }
6248
6249 uint32_t nof_elements = 0;
6250 for (int i = objects.length() - 1; i >= 0; i--) {
6251 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006252 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006253 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006254
6255 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
6256 nof_elements = JSObject::kMaxElementCount;
6257 } else {
6258 nof_elements += encountered_elements;
6259 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006260 }
6261
6262 return nof_elements;
6263}
6264
6265
6266/**
6267 * A helper function of Runtime_ArrayConcat.
6268 *
6269 * The first argument is an Array of arrays and objects. It is the
6270 * same as the arguments array of Array::concat JS function.
6271 *
6272 * If an argument is an Array object, the function visits array
6273 * elements. If an argument is not an Array object, the function
6274 * visits the object as if it is an one-element array.
6275 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006276 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006277 * non-negative number is used as new length. For example, if one
6278 * array length is 2^32 - 1, second array length is 1, the
6279 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006280 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
6281 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006282 */
6283static uint32_t IterateArguments(Handle<JSArray> arguments,
6284 ArrayConcatVisitor* visitor) {
6285 uint32_t visited_elements = 0;
6286 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
6287
6288 for (uint32_t i = 0; i < num_of_args; i++) {
6289 Handle<Object> obj(arguments->GetElement(i));
6290 if (obj->IsJSArray()) {
6291 Handle<JSArray> array = Handle<JSArray>::cast(obj);
6292 uint32_t len = static_cast<uint32_t>(array->length()->Number());
6293 uint32_t nof_elements =
6294 IterateArrayAndPrototypeElements(array, visitor);
6295 // Total elements of array and its prototype chain can be more than
6296 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006297 // the array length number of elements. We use the length as an estimate
6298 // for the actual number of elements added.
6299 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
6300 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
6301 visited_elements = JSArray::kMaxElementCount;
6302 } else {
6303 visited_elements += added_elements;
6304 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006305 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006306 } else {
6307 if (visitor) {
6308 visitor->visit(0, obj);
6309 visitor->increase_index_offset(1);
6310 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006311 if (visited_elements < JSArray::kMaxElementCount) {
6312 visited_elements++;
6313 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006314 }
6315 }
6316 return visited_elements;
6317}
6318
6319
6320/**
6321 * Array::concat implementation.
6322 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006323 * TODO(lrn): Fix non-compliance for very large concatenations and update to
6324 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006325 */
6326static Object* Runtime_ArrayConcat(Arguments args) {
6327 ASSERT(args.length() == 1);
6328 HandleScope handle_scope;
6329
6330 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
6331 Handle<JSArray> arguments(arg_arrays);
6332
6333 // Pass 1: estimate the number of elements of the result
6334 // (it could be more than real numbers if prototype has elements).
6335 uint32_t result_length = 0;
6336 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
6337
6338 { AssertNoAllocation nogc;
6339 for (uint32_t i = 0; i < num_of_args; i++) {
6340 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006341 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006342 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006343 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006344 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
6345 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006346 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006347 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006348 if (JSObject::kMaxElementCount - result_length < length_estimate) {
6349 result_length = JSObject::kMaxElementCount;
6350 break;
6351 }
6352 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006353 }
6354 }
6355
6356 // Allocate an empty array, will set length and content later.
6357 Handle<JSArray> result = Factory::NewJSArray(0);
6358
6359 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
6360 // If estimated number of elements is more than half of length, a
6361 // fixed array (fast case) is more time and space-efficient than a
6362 // dictionary.
6363 bool fast_case = (estimate_nof_elements * 2) >= result_length;
6364
6365 Handle<FixedArray> storage;
6366 if (fast_case) {
6367 // The backing storage array must have non-existing elements to
6368 // preserve holes across concat operations.
6369 storage = Factory::NewFixedArrayWithHoles(result_length);
6370
6371 } else {
6372 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
6373 uint32_t at_least_space_for = estimate_nof_elements +
6374 (estimate_nof_elements >> 2);
6375 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006376 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006377 }
6378
6379 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
6380
6381 ArrayConcatVisitor visitor(storage, result_length, fast_case);
6382
6383 IterateArguments(arguments, &visitor);
6384
6385 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00006386 // Please note the storage might have changed in the visitor.
6387 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006388
6389 return *result;
6390}
6391
6392
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006393// This will not allocate (flatten the string), but it may run
6394// very slowly for very deeply nested ConsStrings. For debugging use only.
6395static Object* Runtime_GlobalPrint(Arguments args) {
6396 NoHandleAllocation ha;
6397 ASSERT(args.length() == 1);
6398
6399 CONVERT_CHECKED(String, string, args[0]);
6400 StringInputBuffer buffer(string);
6401 while (buffer.has_more()) {
6402 uint16_t character = buffer.GetNext();
6403 PrintF("%c", character);
6404 }
6405 return string;
6406}
6407
ager@chromium.org5ec48922009-05-05 07:25:34 +00006408// Moves all own elements of an object, that are below a limit, to positions
6409// starting at zero. All undefined values are placed after non-undefined values,
6410// and are followed by non-existing element. Does not change the length
6411// property.
6412// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006413static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00006414 ASSERT(args.length() == 2);
6415 CONVERT_CHECKED(JSObject, object, args[0]);
6416 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
6417 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006418}
6419
6420
6421// Move contents of argument 0 (an array) to argument 1 (an array)
6422static Object* Runtime_MoveArrayContents(Arguments args) {
6423 ASSERT(args.length() == 2);
6424 CONVERT_CHECKED(JSArray, from, args[0]);
6425 CONVERT_CHECKED(JSArray, to, args[1]);
6426 to->SetContent(FixedArray::cast(from->elements()));
6427 to->set_length(from->length());
6428 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006429 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006430 return to;
6431}
6432
6433
6434// How many elements does this array have?
6435static Object* Runtime_EstimateNumberOfElements(Arguments args) {
6436 ASSERT(args.length() == 1);
6437 CONVERT_CHECKED(JSArray, array, args[0]);
6438 HeapObject* elements = array->elements();
6439 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006440 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006441 } else {
6442 return array->length();
6443 }
6444}
6445
6446
6447// Returns an array that tells you where in the [0, length) interval an array
6448// might have elements. Can either return keys or intervals. Keys can have
6449// gaps in (undefined). Intervals can also span over some undefined keys.
6450static Object* Runtime_GetArrayKeys(Arguments args) {
6451 ASSERT(args.length() == 2);
6452 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00006453 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006454 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006455 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006456 // Create an array and get all the keys into it, then remove all the
6457 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006458 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006459 int keys_length = keys->length();
6460 for (int i = 0; i < keys_length; i++) {
6461 Object* key = keys->get(i);
6462 uint32_t index;
6463 if (!Array::IndexFromObject(key, &index) || index >= length) {
6464 // Zap invalid keys.
6465 keys->set_undefined(i);
6466 }
6467 }
6468 return *Factory::NewJSArrayWithElements(keys);
6469 } else {
6470 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
6471 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006472 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00006473 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
6474 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006475 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00006476 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006477 single_interval->set(1, *length_object);
6478 return *Factory::NewJSArrayWithElements(single_interval);
6479 }
6480}
6481
6482
6483// DefineAccessor takes an optional final argument which is the
6484// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
6485// to the way accessors are implemented, it is set for both the getter
6486// and setter on the first call to DefineAccessor and ignored on
6487// subsequent calls.
6488static Object* Runtime_DefineAccessor(Arguments args) {
6489 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
6490 // Compute attributes.
6491 PropertyAttributes attributes = NONE;
6492 if (args.length() == 5) {
6493 CONVERT_CHECKED(Smi, attrs, args[4]);
6494 int value = attrs->value();
6495 // Only attribute bits should be set.
6496 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
6497 attributes = static_cast<PropertyAttributes>(value);
6498 }
6499
6500 CONVERT_CHECKED(JSObject, obj, args[0]);
6501 CONVERT_CHECKED(String, name, args[1]);
6502 CONVERT_CHECKED(Smi, flag, args[2]);
6503 CONVERT_CHECKED(JSFunction, fun, args[3]);
6504 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
6505}
6506
6507
6508static Object* Runtime_LookupAccessor(Arguments args) {
6509 ASSERT(args.length() == 3);
6510 CONVERT_CHECKED(JSObject, obj, args[0]);
6511 CONVERT_CHECKED(String, name, args[1]);
6512 CONVERT_CHECKED(Smi, flag, args[2]);
6513 return obj->LookupAccessor(name, flag->value() == 0);
6514}
6515
6516
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006517#ifdef ENABLE_DEBUGGER_SUPPORT
6518static Object* Runtime_DebugBreak(Arguments args) {
6519 ASSERT(args.length() == 0);
6520 return Execution::DebugBreakHelper();
6521}
6522
6523
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006524// Helper functions for wrapping and unwrapping stack frame ids.
6525static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006526 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006527 return Smi::FromInt(id >> 2);
6528}
6529
6530
6531static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
6532 return static_cast<StackFrame::Id>(wrapped->value() << 2);
6533}
6534
6535
6536// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00006537// args[0]: debug event listener function to set or null or undefined for
6538// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006539// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00006540static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006541 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006542 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
6543 args[0]->IsUndefined() ||
6544 args[0]->IsNull());
6545 Handle<Object> callback = args.at<Object>(0);
6546 Handle<Object> data = args.at<Object>(1);
6547 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006548
6549 return Heap::undefined_value();
6550}
6551
6552
6553static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006554 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006555 StackGuard::DebugBreak();
6556 return Heap::undefined_value();
6557}
6558
6559
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006560static Object* DebugLookupResultValue(Object* receiver, String* name,
6561 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00006562 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006563 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006564 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006565 case NORMAL:
6566 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006567 if (value->IsTheHole()) {
6568 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006569 }
6570 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006571 case FIELD:
6572 value =
6573 JSObject::cast(
6574 result->holder())->FastPropertyAt(result->GetFieldIndex());
6575 if (value->IsTheHole()) {
6576 return Heap::undefined_value();
6577 }
6578 return value;
6579 case CONSTANT_FUNCTION:
6580 return result->GetConstantFunction();
6581 case CALLBACKS: {
6582 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006583 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006584 value = receiver->GetPropertyWithCallback(
6585 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00006586 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006587 value = Top::pending_exception();
6588 Top::clear_pending_exception();
6589 if (caught_exception != NULL) {
6590 *caught_exception = true;
6591 }
6592 }
6593 return value;
6594 } else {
6595 return Heap::undefined_value();
6596 }
6597 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006598 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006599 case MAP_TRANSITION:
6600 case CONSTANT_TRANSITION:
6601 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006602 return Heap::undefined_value();
6603 default:
6604 UNREACHABLE();
6605 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006606 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006607 return Heap::undefined_value();
6608}
6609
6610
ager@chromium.org32912102009-01-16 10:38:43 +00006611// Get debugger related details for an object property.
6612// args[0]: object holding property
6613// args[1]: name of the property
6614//
6615// The array returned contains the following information:
6616// 0: Property value
6617// 1: Property details
6618// 2: Property value is exception
6619// 3: Getter function if defined
6620// 4: Setter function if defined
6621// Items 2-4 are only filled if the property has either a getter or a setter
6622// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006623static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006624 HandleScope scope;
6625
6626 ASSERT(args.length() == 2);
6627
6628 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6629 CONVERT_ARG_CHECKED(String, name, 1);
6630
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006631 // Make sure to set the current context to the context before the debugger was
6632 // entered (if the debugger is entered). The reason for switching context here
6633 // is that for some property lookups (accessors and interceptors) callbacks
6634 // into the embedding application can occour, and the embedding application
6635 // could have the assumption that its own global context is the current
6636 // context and not some internal debugger context.
6637 SaveContext save;
6638 if (Debug::InDebugger()) {
6639 Top::set_context(*Debug::debugger_entry()->GetContext());
6640 }
6641
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006642 // Skip the global proxy as it has no properties and always delegates to the
6643 // real global object.
6644 if (obj->IsJSGlobalProxy()) {
6645 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6646 }
6647
6648
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006649 // Check if the name is trivially convertible to an index and get the element
6650 // if so.
6651 uint32_t index;
6652 if (name->AsArrayIndex(&index)) {
6653 Handle<FixedArray> details = Factory::NewFixedArray(2);
6654 details->set(0, Runtime::GetElementOrCharAt(obj, index));
6655 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
6656 return *Factory::NewJSArrayWithElements(details);
6657 }
6658
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006659 // Find the number of objects making up this.
6660 int length = LocalPrototypeChainLength(*obj);
6661
6662 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006663 Handle<JSObject> jsproto = obj;
6664 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006665 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006666 jsproto->LocalLookup(*name, &result);
6667 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006668 // LookupResult is not GC safe as it holds raw object pointers.
6669 // GC can happen later in this code so put the required fields into
6670 // local variables using handles when required for later use.
6671 PropertyType result_type = result.type();
6672 Handle<Object> result_callback_obj;
6673 if (result_type == CALLBACKS) {
6674 result_callback_obj = Handle<Object>(result.GetCallbackObject());
6675 }
6676 Smi* property_details = result.GetPropertyDetails().AsSmi();
6677 // DebugLookupResultValue can cause GC so details from LookupResult needs
6678 // to be copied to handles before this.
6679 bool caught_exception = false;
6680 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
6681 &caught_exception);
6682 if (raw_value->IsFailure()) return raw_value;
6683 Handle<Object> value(raw_value);
6684
6685 // If the callback object is a fixed array then it contains JavaScript
6686 // getter and/or setter.
6687 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
6688 result_callback_obj->IsFixedArray();
6689 Handle<FixedArray> details =
6690 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
6691 details->set(0, *value);
6692 details->set(1, property_details);
6693 if (hasJavaScriptAccessors) {
6694 details->set(2,
6695 caught_exception ? Heap::true_value()
6696 : Heap::false_value());
6697 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
6698 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
6699 }
6700
6701 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006702 }
6703 if (i < length - 1) {
6704 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6705 }
6706 }
6707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006708 return Heap::undefined_value();
6709}
6710
6711
6712static Object* Runtime_DebugGetProperty(Arguments args) {
6713 HandleScope scope;
6714
6715 ASSERT(args.length() == 2);
6716
6717 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6718 CONVERT_ARG_CHECKED(String, name, 1);
6719
6720 LookupResult result;
6721 obj->Lookup(*name, &result);
6722 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006723 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006724 }
6725 return Heap::undefined_value();
6726}
6727
6728
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006729// Return the property type calculated from the property details.
6730// args[0]: smi with property details.
6731static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6732 ASSERT(args.length() == 1);
6733 CONVERT_CHECKED(Smi, details, args[0]);
6734 PropertyType type = PropertyDetails(details).type();
6735 return Smi::FromInt(static_cast<int>(type));
6736}
6737
6738
6739// Return the property attribute calculated from the property details.
6740// args[0]: smi with property details.
6741static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6742 ASSERT(args.length() == 1);
6743 CONVERT_CHECKED(Smi, details, args[0]);
6744 PropertyAttributes attributes = PropertyDetails(details).attributes();
6745 return Smi::FromInt(static_cast<int>(attributes));
6746}
6747
6748
6749// Return the property insertion index calculated from the property details.
6750// args[0]: smi with property details.
6751static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6752 ASSERT(args.length() == 1);
6753 CONVERT_CHECKED(Smi, details, args[0]);
6754 int index = PropertyDetails(details).index();
6755 return Smi::FromInt(index);
6756}
6757
6758
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006759// Return property value from named interceptor.
6760// args[0]: object
6761// args[1]: property name
6762static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6763 HandleScope scope;
6764 ASSERT(args.length() == 2);
6765 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6766 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6767 CONVERT_ARG_CHECKED(String, name, 1);
6768
6769 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006770 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006771}
6772
6773
6774// Return element value from indexed interceptor.
6775// args[0]: object
6776// args[1]: index
6777static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6778 HandleScope scope;
6779 ASSERT(args.length() == 2);
6780 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6781 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6782 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6783
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006784 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006785}
6786
6787
6788static Object* Runtime_CheckExecutionState(Arguments args) {
6789 ASSERT(args.length() >= 1);
6790 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006791 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006792 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793 return Top::Throw(Heap::illegal_execution_state_symbol());
6794 }
6795
6796 return Heap::true_value();
6797}
6798
6799
6800static Object* Runtime_GetFrameCount(Arguments args) {
6801 HandleScope scope;
6802 ASSERT(args.length() == 1);
6803
6804 // Check arguments.
6805 Object* result = Runtime_CheckExecutionState(args);
6806 if (result->IsFailure()) return result;
6807
6808 // Count all frames which are relevant to debugging stack trace.
6809 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006810 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006811 if (id == StackFrame::NO_ID) {
6812 // If there is no JavaScript stack frame count is 0.
6813 return Smi::FromInt(0);
6814 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006815 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6816 return Smi::FromInt(n);
6817}
6818
6819
6820static const int kFrameDetailsFrameIdIndex = 0;
6821static const int kFrameDetailsReceiverIndex = 1;
6822static const int kFrameDetailsFunctionIndex = 2;
6823static const int kFrameDetailsArgumentCountIndex = 3;
6824static const int kFrameDetailsLocalCountIndex = 4;
6825static const int kFrameDetailsSourcePositionIndex = 5;
6826static const int kFrameDetailsConstructCallIndex = 6;
6827static const int kFrameDetailsDebuggerFrameIndex = 7;
6828static const int kFrameDetailsFirstDynamicIndex = 8;
6829
6830// Return an array with frame details
6831// args[0]: number: break id
6832// args[1]: number: frame index
6833//
6834// The array returned contains the following information:
6835// 0: Frame id
6836// 1: Receiver
6837// 2: Function
6838// 3: Argument count
6839// 4: Local count
6840// 5: Source position
6841// 6: Constructor call
6842// 7: Debugger frame
6843// Arguments name, value
6844// Locals name, value
6845static Object* Runtime_GetFrameDetails(Arguments args) {
6846 HandleScope scope;
6847 ASSERT(args.length() == 2);
6848
6849 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006850 Object* check = Runtime_CheckExecutionState(args);
6851 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006852 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6853
6854 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006855 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006856 if (id == StackFrame::NO_ID) {
6857 // If there are no JavaScript stack frames return undefined.
6858 return Heap::undefined_value();
6859 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006860 int count = 0;
6861 JavaScriptFrameIterator it(id);
6862 for (; !it.done(); it.Advance()) {
6863 if (count == index) break;
6864 count++;
6865 }
6866 if (it.done()) return Heap::undefined_value();
6867
6868 // Traverse the saved contexts chain to find the active context for the
6869 // selected frame.
6870 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006871 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006872 save = save->prev();
6873 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006874 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006875
6876 // Get the frame id.
6877 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6878
6879 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006880 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006881
6882 // Check for constructor frame.
6883 bool constructor = it.frame()->IsConstructor();
6884
6885 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006886 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006887 ScopeInfo<> info(*code);
6888
6889 // Get the context.
6890 Handle<Context> context(Context::cast(it.frame()->context()));
6891
6892 // Get the locals names and values into a temporary array.
6893 //
6894 // TODO(1240907): Hide compiler-introduced stack variables
6895 // (e.g. .result)? For users of the debugger, they will probably be
6896 // confusing.
6897 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6898 for (int i = 0; i < info.NumberOfLocals(); i++) {
6899 // Name of the local.
6900 locals->set(i * 2, *info.LocalName(i));
6901
6902 // Fetch the value of the local - either from the stack or from a
6903 // heap-allocated context.
6904 if (i < info.number_of_stack_slots()) {
6905 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6906 } else {
6907 Handle<String> name = info.LocalName(i);
6908 // Traverse the context chain to the function context as all local
6909 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006910 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006911 context = Handle<Context>(context->previous());
6912 }
6913 ASSERT(context->is_function_context());
6914 locals->set(i * 2 + 1,
6915 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6916 NULL)));
6917 }
6918 }
6919
6920 // Now advance to the arguments adapter frame (if any). If contains all
6921 // the provided parameters and
6922
6923 // Now advance to the arguments adapter frame (if any). It contains all
6924 // the provided parameters whereas the function frame always have the number
6925 // of arguments matching the functions parameters. The rest of the
6926 // information (except for what is collected above) is the same.
6927 it.AdvanceToArgumentsFrame();
6928
6929 // Find the number of arguments to fill. At least fill the number of
6930 // parameters for the function and fill more if more parameters are provided.
6931 int argument_count = info.number_of_parameters();
6932 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6933 argument_count = it.frame()->GetProvidedParametersCount();
6934 }
6935
6936 // Calculate the size of the result.
6937 int details_size = kFrameDetailsFirstDynamicIndex +
6938 2 * (argument_count + info.NumberOfLocals());
6939 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6940
6941 // Add the frame id.
6942 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6943
6944 // Add the function (same as in function frame).
6945 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6946
6947 // Add the arguments count.
6948 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6949
6950 // Add the locals count
6951 details->set(kFrameDetailsLocalCountIndex,
6952 Smi::FromInt(info.NumberOfLocals()));
6953
6954 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006955 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006956 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6957 } else {
6958 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6959 }
6960
6961 // Add the constructor information.
6962 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6963
6964 // Add information on whether this frame is invoked in the debugger context.
6965 details->set(kFrameDetailsDebuggerFrameIndex,
6966 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6967
6968 // Fill the dynamic part.
6969 int details_index = kFrameDetailsFirstDynamicIndex;
6970
6971 // Add arguments name and value.
6972 for (int i = 0; i < argument_count; i++) {
6973 // Name of the argument.
6974 if (i < info.number_of_parameters()) {
6975 details->set(details_index++, *info.parameter_name(i));
6976 } else {
6977 details->set(details_index++, Heap::undefined_value());
6978 }
6979
6980 // Parameter value.
6981 if (i < it.frame()->GetProvidedParametersCount()) {
6982 details->set(details_index++, it.frame()->GetParameter(i));
6983 } else {
6984 details->set(details_index++, Heap::undefined_value());
6985 }
6986 }
6987
6988 // Add locals name and value from the temporary copy from the function frame.
6989 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6990 details->set(details_index++, locals->get(i));
6991 }
6992
6993 // Add the receiver (same as in function frame).
6994 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6995 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6996 Handle<Object> receiver(it.frame()->receiver());
6997 if (!receiver->IsJSObject()) {
6998 // If the receiver is NOT a JSObject we have hit an optimization
6999 // where a value object is not converted into a wrapped JS objects.
7000 // To hide this optimization from the debugger, we wrap the receiver
7001 // by creating correct wrapper object based on the calling frame's
7002 // global context.
7003 it.Advance();
7004 Handle<Context> calling_frames_global_context(
7005 Context::cast(Context::cast(it.frame()->context())->global_context()));
7006 receiver = Factory::ToObject(receiver, calling_frames_global_context);
7007 }
7008 details->set(kFrameDetailsReceiverIndex, *receiver);
7009
7010 ASSERT_EQ(details_size, details_index);
7011 return *Factory::NewJSArrayWithElements(details);
7012}
7013
7014
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007015// Copy all the context locals into an object used to materialize a scope.
7016static void CopyContextLocalsToScopeObject(Handle<Code> code,
7017 ScopeInfo<>& scope_info,
7018 Handle<Context> context,
7019 Handle<JSObject> scope_object) {
7020 // Fill all context locals to the context extension.
7021 for (int i = Context::MIN_CONTEXT_SLOTS;
7022 i < scope_info.number_of_context_slots();
7023 i++) {
7024 int context_index =
7025 ScopeInfo<>::ContextSlotIndex(*code,
7026 *scope_info.context_slot_name(i),
7027 NULL);
7028
7029 // Don't include the arguments shadow (.arguments) context variable.
7030 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
7031 SetProperty(scope_object,
7032 scope_info.context_slot_name(i),
7033 Handle<Object>(context->get(context_index)), NONE);
7034 }
7035 }
7036}
7037
7038
7039// Create a plain JSObject which materializes the local scope for the specified
7040// frame.
7041static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
7042 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7043 Handle<Code> code(function->code());
7044 ScopeInfo<> scope_info(*code);
7045
7046 // Allocate and initialize a JSObject with all the arguments, stack locals
7047 // heap locals and extension properties of the debugged function.
7048 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
7049
7050 // First fill all parameters.
7051 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
7052 SetProperty(local_scope,
7053 scope_info.parameter_name(i),
7054 Handle<Object>(frame->GetParameter(i)), NONE);
7055 }
7056
7057 // Second fill all stack locals.
7058 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
7059 SetProperty(local_scope,
7060 scope_info.stack_slot_name(i),
7061 Handle<Object>(frame->GetExpression(i)), NONE);
7062 }
7063
7064 // Third fill all context locals.
7065 Handle<Context> frame_context(Context::cast(frame->context()));
7066 Handle<Context> function_context(frame_context->fcontext());
7067 CopyContextLocalsToScopeObject(code, scope_info,
7068 function_context, local_scope);
7069
7070 // Finally copy any properties from the function context extension. This will
7071 // be variables introduced by eval.
7072 if (function_context->closure() == *function) {
7073 if (function_context->has_extension() &&
7074 !function_context->IsGlobalContext()) {
7075 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007076 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007077 for (int i = 0; i < keys->length(); i++) {
7078 // Names of variables introduced by eval are strings.
7079 ASSERT(keys->get(i)->IsString());
7080 Handle<String> key(String::cast(keys->get(i)));
7081 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
7082 }
7083 }
7084 }
7085 return local_scope;
7086}
7087
7088
7089// Create a plain JSObject which materializes the closure content for the
7090// context.
7091static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
7092 ASSERT(context->is_function_context());
7093
7094 Handle<Code> code(context->closure()->code());
7095 ScopeInfo<> scope_info(*code);
7096
7097 // Allocate and initialize a JSObject with all the content of theis function
7098 // closure.
7099 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
7100
7101 // Check whether the arguments shadow object exists.
7102 int arguments_shadow_index =
7103 ScopeInfo<>::ContextSlotIndex(*code,
7104 Heap::arguments_shadow_symbol(),
7105 NULL);
7106 if (arguments_shadow_index >= 0) {
7107 // In this case all the arguments are available in the arguments shadow
7108 // object.
7109 Handle<JSObject> arguments_shadow(
7110 JSObject::cast(context->get(arguments_shadow_index)));
7111 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
7112 SetProperty(closure_scope,
7113 scope_info.parameter_name(i),
7114 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
7115 }
7116 }
7117
7118 // Fill all context locals to the context extension.
7119 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
7120
7121 // Finally copy any properties from the function context extension. This will
7122 // be variables introduced by eval.
7123 if (context->has_extension()) {
7124 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007125 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007126 for (int i = 0; i < keys->length(); i++) {
7127 // Names of variables introduced by eval are strings.
7128 ASSERT(keys->get(i)->IsString());
7129 Handle<String> key(String::cast(keys->get(i)));
7130 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
7131 }
7132 }
7133
7134 return closure_scope;
7135}
7136
7137
7138// Iterate over the actual scopes visible from a stack frame. All scopes are
7139// backed by an actual context except the local scope, which is inserted
7140// "artifically" in the context chain.
7141class ScopeIterator {
7142 public:
7143 enum ScopeType {
7144 ScopeTypeGlobal = 0,
7145 ScopeTypeLocal,
7146 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00007147 ScopeTypeClosure,
7148 // Every catch block contains an implicit with block (its parameter is
7149 // a JSContextExtensionObject) that extends current scope with a variable
7150 // holding exception object. Such with blocks are treated as scopes of their
7151 // own type.
7152 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007153 };
7154
7155 explicit ScopeIterator(JavaScriptFrame* frame)
7156 : frame_(frame),
7157 function_(JSFunction::cast(frame->function())),
7158 context_(Context::cast(frame->context())),
7159 local_done_(false),
7160 at_local_(false) {
7161
7162 // Check whether the first scope is actually a local scope.
7163 if (context_->IsGlobalContext()) {
7164 // If there is a stack slot for .result then this local scope has been
7165 // created for evaluating top level code and it is not a real local scope.
7166 // Checking for the existence of .result seems fragile, but the scope info
7167 // saved with the code object does not otherwise have that information.
7168 Handle<Code> code(function_->code());
7169 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
7170 at_local_ = index < 0;
7171 } else if (context_->is_function_context()) {
7172 at_local_ = true;
7173 }
7174 }
7175
7176 // More scopes?
7177 bool Done() { return context_.is_null(); }
7178
7179 // Move to the next scope.
7180 void Next() {
7181 // If at a local scope mark the local scope as passed.
7182 if (at_local_) {
7183 at_local_ = false;
7184 local_done_ = true;
7185
7186 // If the current context is not associated with the local scope the
7187 // current context is the next real scope, so don't move to the next
7188 // context in this case.
7189 if (context_->closure() != *function_) {
7190 return;
7191 }
7192 }
7193
7194 // The global scope is always the last in the chain.
7195 if (context_->IsGlobalContext()) {
7196 context_ = Handle<Context>();
7197 return;
7198 }
7199
7200 // Move to the next context.
7201 if (context_->is_function_context()) {
7202 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
7203 } else {
7204 context_ = Handle<Context>(context_->previous());
7205 }
7206
7207 // If passing the local scope indicate that the current scope is now the
7208 // local scope.
7209 if (!local_done_ &&
7210 (context_->IsGlobalContext() || (context_->is_function_context()))) {
7211 at_local_ = true;
7212 }
7213 }
7214
7215 // Return the type of the current scope.
7216 int Type() {
7217 if (at_local_) {
7218 return ScopeTypeLocal;
7219 }
7220 if (context_->IsGlobalContext()) {
7221 ASSERT(context_->global()->IsGlobalObject());
7222 return ScopeTypeGlobal;
7223 }
7224 if (context_->is_function_context()) {
7225 return ScopeTypeClosure;
7226 }
7227 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00007228 // Current scope is either an explicit with statement or a with statement
7229 // implicitely generated for a catch block.
7230 // If the extension object here is a JSContextExtensionObject then
7231 // current with statement is one frome a catch block otherwise it's a
7232 // regular with statement.
7233 if (context_->extension()->IsJSContextExtensionObject()) {
7234 return ScopeTypeCatch;
7235 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007236 return ScopeTypeWith;
7237 }
7238
7239 // Return the JavaScript object with the content of the current scope.
7240 Handle<JSObject> ScopeObject() {
7241 switch (Type()) {
7242 case ScopeIterator::ScopeTypeGlobal:
7243 return Handle<JSObject>(CurrentContext()->global());
7244 break;
7245 case ScopeIterator::ScopeTypeLocal:
7246 // Materialize the content of the local scope into a JSObject.
7247 return MaterializeLocalScope(frame_);
7248 break;
7249 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00007250 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007251 // Return the with object.
7252 return Handle<JSObject>(CurrentContext()->extension());
7253 break;
7254 case ScopeIterator::ScopeTypeClosure:
7255 // Materialize the content of the closure scope into a JSObject.
7256 return MaterializeClosure(CurrentContext());
7257 break;
7258 }
7259 UNREACHABLE();
7260 return Handle<JSObject>();
7261 }
7262
7263 // Return the context for this scope. For the local context there might not
7264 // be an actual context.
7265 Handle<Context> CurrentContext() {
7266 if (at_local_ && context_->closure() != *function_) {
7267 return Handle<Context>();
7268 }
7269 return context_;
7270 }
7271
7272#ifdef DEBUG
7273 // Debug print of the content of the current scope.
7274 void DebugPrint() {
7275 switch (Type()) {
7276 case ScopeIterator::ScopeTypeGlobal:
7277 PrintF("Global:\n");
7278 CurrentContext()->Print();
7279 break;
7280
7281 case ScopeIterator::ScopeTypeLocal: {
7282 PrintF("Local:\n");
7283 Handle<Code> code(function_->code());
7284 ScopeInfo<> scope_info(*code);
7285 scope_info.Print();
7286 if (!CurrentContext().is_null()) {
7287 CurrentContext()->Print();
7288 if (CurrentContext()->has_extension()) {
7289 Handle<JSObject> extension =
7290 Handle<JSObject>(CurrentContext()->extension());
7291 if (extension->IsJSContextExtensionObject()) {
7292 extension->Print();
7293 }
7294 }
7295 }
7296 break;
7297 }
7298
7299 case ScopeIterator::ScopeTypeWith: {
7300 PrintF("With:\n");
7301 Handle<JSObject> extension =
7302 Handle<JSObject>(CurrentContext()->extension());
7303 extension->Print();
7304 break;
7305 }
7306
ager@chromium.orga1645e22009-09-09 19:27:10 +00007307 case ScopeIterator::ScopeTypeCatch: {
7308 PrintF("Catch:\n");
7309 Handle<JSObject> extension =
7310 Handle<JSObject>(CurrentContext()->extension());
7311 extension->Print();
7312 break;
7313 }
7314
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007315 case ScopeIterator::ScopeTypeClosure: {
7316 PrintF("Closure:\n");
7317 CurrentContext()->Print();
7318 if (CurrentContext()->has_extension()) {
7319 Handle<JSObject> extension =
7320 Handle<JSObject>(CurrentContext()->extension());
7321 if (extension->IsJSContextExtensionObject()) {
7322 extension->Print();
7323 }
7324 }
7325 break;
7326 }
7327
7328 default:
7329 UNREACHABLE();
7330 }
7331 PrintF("\n");
7332 }
7333#endif
7334
7335 private:
7336 JavaScriptFrame* frame_;
7337 Handle<JSFunction> function_;
7338 Handle<Context> context_;
7339 bool local_done_;
7340 bool at_local_;
7341
7342 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
7343};
7344
7345
7346static Object* Runtime_GetScopeCount(Arguments args) {
7347 HandleScope scope;
7348 ASSERT(args.length() == 2);
7349
7350 // Check arguments.
7351 Object* check = Runtime_CheckExecutionState(args);
7352 if (check->IsFailure()) return check;
7353 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7354
7355 // Get the frame where the debugging is performed.
7356 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7357 JavaScriptFrameIterator it(id);
7358 JavaScriptFrame* frame = it.frame();
7359
7360 // Count the visible scopes.
7361 int n = 0;
7362 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
7363 n++;
7364 }
7365
7366 return Smi::FromInt(n);
7367}
7368
7369
7370static const int kScopeDetailsTypeIndex = 0;
7371static const int kScopeDetailsObjectIndex = 1;
7372static const int kScopeDetailsSize = 2;
7373
7374// Return an array with scope details
7375// args[0]: number: break id
7376// args[1]: number: frame index
7377// args[2]: number: scope index
7378//
7379// The array returned contains the following information:
7380// 0: Scope type
7381// 1: Scope object
7382static Object* Runtime_GetScopeDetails(Arguments args) {
7383 HandleScope scope;
7384 ASSERT(args.length() == 3);
7385
7386 // Check arguments.
7387 Object* check = Runtime_CheckExecutionState(args);
7388 if (check->IsFailure()) return check;
7389 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7390 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
7391
7392 // Get the frame where the debugging is performed.
7393 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7394 JavaScriptFrameIterator frame_it(id);
7395 JavaScriptFrame* frame = frame_it.frame();
7396
7397 // Find the requested scope.
7398 int n = 0;
7399 ScopeIterator it(frame);
7400 for (; !it.Done() && n < index; it.Next()) {
7401 n++;
7402 }
7403 if (it.Done()) {
7404 return Heap::undefined_value();
7405 }
7406
7407 // Calculate the size of the result.
7408 int details_size = kScopeDetailsSize;
7409 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
7410
7411 // Fill in scope details.
7412 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
7413 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
7414
7415 return *Factory::NewJSArrayWithElements(details);
7416}
7417
7418
7419static Object* Runtime_DebugPrintScopes(Arguments args) {
7420 HandleScope scope;
7421 ASSERT(args.length() == 0);
7422
7423#ifdef DEBUG
7424 // Print the scopes for the top frame.
7425 StackFrameLocator locator;
7426 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
7427 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
7428 it.DebugPrint();
7429 }
7430#endif
7431 return Heap::undefined_value();
7432}
7433
7434
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007435static Object* Runtime_GetCFrames(Arguments args) {
7436 HandleScope scope;
7437 ASSERT(args.length() == 1);
7438 Object* result = Runtime_CheckExecutionState(args);
7439 if (result->IsFailure()) return result;
7440
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007441#if V8_HOST_ARCH_64_BIT
7442 UNIMPLEMENTED();
7443 return Heap::undefined_value();
7444#else
7445
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007446 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007447 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
7448 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007449 if (frames_count == OS::kStackWalkError) {
7450 return Heap::undefined_value();
7451 }
7452
7453 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
7454 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
7455 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
7456 for (int i = 0; i < frames_count; i++) {
7457 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
7458 frame_value->SetProperty(
7459 *address_str,
7460 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
7461 NONE);
7462
7463 // Get the stack walk text for this frame.
7464 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007465 int frame_text_length = StrLength(frames[i].text);
7466 if (frame_text_length > 0) {
7467 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007468 frame_text = Factory::NewStringFromAscii(str);
7469 }
7470
7471 if (!frame_text.is_null()) {
7472 frame_value->SetProperty(*text_str, *frame_text, NONE);
7473 }
7474
7475 frames_array->set(i, *frame_value);
7476 }
7477 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007478#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007479}
7480
7481
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007482static Object* Runtime_GetThreadCount(Arguments args) {
7483 HandleScope scope;
7484 ASSERT(args.length() == 1);
7485
7486 // Check arguments.
7487 Object* result = Runtime_CheckExecutionState(args);
7488 if (result->IsFailure()) return result;
7489
7490 // Count all archived V8 threads.
7491 int n = 0;
7492 for (ThreadState* thread = ThreadState::FirstInUse();
7493 thread != NULL;
7494 thread = thread->Next()) {
7495 n++;
7496 }
7497
7498 // Total number of threads is current thread and archived threads.
7499 return Smi::FromInt(n + 1);
7500}
7501
7502
7503static const int kThreadDetailsCurrentThreadIndex = 0;
7504static const int kThreadDetailsThreadIdIndex = 1;
7505static const int kThreadDetailsSize = 2;
7506
7507// Return an array with thread details
7508// args[0]: number: break id
7509// args[1]: number: thread index
7510//
7511// The array returned contains the following information:
7512// 0: Is current thread?
7513// 1: Thread id
7514static Object* Runtime_GetThreadDetails(Arguments args) {
7515 HandleScope scope;
7516 ASSERT(args.length() == 2);
7517
7518 // Check arguments.
7519 Object* check = Runtime_CheckExecutionState(args);
7520 if (check->IsFailure()) return check;
7521 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
7522
7523 // Allocate array for result.
7524 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
7525
7526 // Thread index 0 is current thread.
7527 if (index == 0) {
7528 // Fill the details.
7529 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
7530 details->set(kThreadDetailsThreadIdIndex,
7531 Smi::FromInt(ThreadManager::CurrentId()));
7532 } else {
7533 // Find the thread with the requested index.
7534 int n = 1;
7535 ThreadState* thread = ThreadState::FirstInUse();
7536 while (index != n && thread != NULL) {
7537 thread = thread->Next();
7538 n++;
7539 }
7540 if (thread == NULL) {
7541 return Heap::undefined_value();
7542 }
7543
7544 // Fill the details.
7545 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
7546 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
7547 }
7548
7549 // Convert to JS array and return.
7550 return *Factory::NewJSArrayWithElements(details);
7551}
7552
7553
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007554static Object* Runtime_GetBreakLocations(Arguments args) {
7555 HandleScope scope;
7556 ASSERT(args.length() == 1);
7557
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007558 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7559 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007560 // Find the number of break points
7561 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
7562 if (break_locations->IsUndefined()) return Heap::undefined_value();
7563 // Return array as JS array
7564 return *Factory::NewJSArrayWithElements(
7565 Handle<FixedArray>::cast(break_locations));
7566}
7567
7568
7569// Set a break point in a function
7570// args[0]: function
7571// args[1]: number: break source position (within the function source)
7572// args[2]: number: break point object
7573static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
7574 HandleScope scope;
7575 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007576 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7577 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007578 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7579 RUNTIME_ASSERT(source_position >= 0);
7580 Handle<Object> break_point_object_arg = args.at<Object>(2);
7581
7582 // Set break point.
7583 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
7584
7585 return Heap::undefined_value();
7586}
7587
7588
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007589Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
7590 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007591 // Iterate the heap looking for SharedFunctionInfo generated from the
7592 // script. The inner most SharedFunctionInfo containing the source position
7593 // for the requested break point is found.
7594 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
7595 // which is found is not compiled it is compiled and the heap is iterated
7596 // again as the compilation might create inner functions from the newly
7597 // compiled function and the actual requested break point might be in one of
7598 // these functions.
7599 bool done = false;
7600 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00007601 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007602 Handle<SharedFunctionInfo> target;
7603 // The current candidate for the last function in script:
7604 Handle<SharedFunctionInfo> last;
7605 while (!done) {
7606 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007607 for (HeapObject* obj = iterator.next();
7608 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007609 if (obj->IsSharedFunctionInfo()) {
7610 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
7611 if (shared->script() == *script) {
7612 // If the SharedFunctionInfo found has the requested script data and
7613 // contains the source position it is a candidate.
7614 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00007615 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007616 start_position = shared->start_position();
7617 }
7618 if (start_position <= position &&
7619 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007620 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007621 // candidate this is the new candidate.
7622 if (target.is_null()) {
7623 target_start_position = start_position;
7624 target = shared;
7625 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007626 if (target_start_position == start_position &&
7627 shared->end_position() == target->end_position()) {
7628 // If a top-level function contain only one function
7629 // declartion the source for the top-level and the function is
7630 // the same. In that case prefer the non top-level function.
7631 if (!shared->is_toplevel()) {
7632 target_start_position = start_position;
7633 target = shared;
7634 }
7635 } else if (target_start_position <= start_position &&
7636 shared->end_position() <= target->end_position()) {
7637 // This containment check includes equality as a function inside
7638 // a top-level function can share either start or end position
7639 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007640 target_start_position = start_position;
7641 target = shared;
7642 }
7643 }
7644 }
7645
7646 // Keep track of the last function in the script.
7647 if (last.is_null() ||
7648 shared->end_position() > last->start_position()) {
7649 last = shared;
7650 }
7651 }
7652 }
7653 }
7654
7655 // Make sure some candidate is selected.
7656 if (target.is_null()) {
7657 if (!last.is_null()) {
7658 // Position after the last function - use last.
7659 target = last;
7660 } else {
7661 // Unable to find function - possibly script without any function.
7662 return Heap::undefined_value();
7663 }
7664 }
7665
7666 // If the candidate found is compiled we are done. NOTE: when lazy
7667 // compilation of inner functions is introduced some additional checking
7668 // needs to be done here to compile inner functions.
7669 done = target->is_compiled();
7670 if (!done) {
7671 // If the candidate is not compiled compile it to reveal any inner
7672 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007673 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007674 }
7675 }
7676
7677 return *target;
7678}
7679
7680
7681// Change the state of a break point in a script. NOTE: Regarding performance
7682// see the NOTE for GetScriptFromScriptData.
7683// args[0]: script to set break point in
7684// args[1]: number: break source position (within the script source)
7685// args[2]: number: break point object
7686static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7687 HandleScope scope;
7688 ASSERT(args.length() == 3);
7689 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7690 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7691 RUNTIME_ASSERT(source_position >= 0);
7692 Handle<Object> break_point_object_arg = args.at<Object>(2);
7693
7694 // Get the script from the script wrapper.
7695 RUNTIME_ASSERT(wrapper->value()->IsScript());
7696 Handle<Script> script(Script::cast(wrapper->value()));
7697
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007698 Object* result = Runtime::FindSharedFunctionInfoInScript(
7699 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007700 if (!result->IsUndefined()) {
7701 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7702 // Find position within function. The script position might be before the
7703 // source position of the first function.
7704 int position;
7705 if (shared->start_position() > source_position) {
7706 position = 0;
7707 } else {
7708 position = source_position - shared->start_position();
7709 }
7710 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7711 }
7712 return Heap::undefined_value();
7713}
7714
7715
7716// Clear a break point
7717// args[0]: number: break point object
7718static Object* Runtime_ClearBreakPoint(Arguments args) {
7719 HandleScope scope;
7720 ASSERT(args.length() == 1);
7721 Handle<Object> break_point_object_arg = args.at<Object>(0);
7722
7723 // Clear break point.
7724 Debug::ClearBreakPoint(break_point_object_arg);
7725
7726 return Heap::undefined_value();
7727}
7728
7729
7730// Change the state of break on exceptions
7731// args[0]: boolean indicating uncaught exceptions
7732// args[1]: boolean indicating on/off
7733static Object* Runtime_ChangeBreakOnException(Arguments args) {
7734 HandleScope scope;
7735 ASSERT(args.length() == 2);
7736 ASSERT(args[0]->IsNumber());
7737 ASSERT(args[1]->IsBoolean());
7738
7739 // Update break point state
7740 ExceptionBreakType type =
7741 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7742 bool enable = args[1]->ToBoolean()->IsTrue();
7743 Debug::ChangeBreakOnException(type, enable);
7744 return Heap::undefined_value();
7745}
7746
7747
7748// Prepare for stepping
7749// args[0]: break id for checking execution state
7750// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007751// args[2]: number of times to perform the step, for step out it is the number
7752// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007753static Object* Runtime_PrepareStep(Arguments args) {
7754 HandleScope scope;
7755 ASSERT(args.length() == 3);
7756 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007757 Object* check = Runtime_CheckExecutionState(args);
7758 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007759 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7760 return Top::Throw(Heap::illegal_argument_symbol());
7761 }
7762
7763 // Get the step action and check validity.
7764 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7765 if (step_action != StepIn &&
7766 step_action != StepNext &&
7767 step_action != StepOut &&
7768 step_action != StepInMin &&
7769 step_action != StepMin) {
7770 return Top::Throw(Heap::illegal_argument_symbol());
7771 }
7772
7773 // Get the number of steps.
7774 int step_count = NumberToInt32(args[2]);
7775 if (step_count < 1) {
7776 return Top::Throw(Heap::illegal_argument_symbol());
7777 }
7778
ager@chromium.orga1645e22009-09-09 19:27:10 +00007779 // Clear all current stepping setup.
7780 Debug::ClearStepping();
7781
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007782 // Prepare step.
7783 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7784 return Heap::undefined_value();
7785}
7786
7787
7788// Clear all stepping set by PrepareStep.
7789static Object* Runtime_ClearStepping(Arguments args) {
7790 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007791 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007792 Debug::ClearStepping();
7793 return Heap::undefined_value();
7794}
7795
7796
7797// Creates a copy of the with context chain. The copy of the context chain is
7798// is linked to the function context supplied.
7799static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7800 Handle<Context> function_context) {
7801 // At the bottom of the chain. Return the function context to link to.
7802 if (context_chain->is_function_context()) {
7803 return function_context;
7804 }
7805
7806 // Recursively copy the with contexts.
7807 Handle<Context> previous(context_chain->previous());
7808 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7809 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007810 CopyWithContextChain(function_context, previous),
7811 extension,
7812 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007813}
7814
7815
7816// Helper function to find or create the arguments object for
7817// Runtime_DebugEvaluate.
7818static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7819 Handle<JSFunction> function,
7820 Handle<Code> code,
7821 const ScopeInfo<>* sinfo,
7822 Handle<Context> function_context) {
7823 // Try to find the value of 'arguments' to pass as parameter. If it is not
7824 // found (that is the debugged function does not reference 'arguments' and
7825 // does not support eval) then create an 'arguments' object.
7826 int index;
7827 if (sinfo->number_of_stack_slots() > 0) {
7828 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7829 if (index != -1) {
7830 return Handle<Object>(frame->GetExpression(index));
7831 }
7832 }
7833
7834 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7835 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7836 NULL);
7837 if (index != -1) {
7838 return Handle<Object>(function_context->get(index));
7839 }
7840 }
7841
7842 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007843 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7844 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007845
7846 AssertNoAllocation no_gc;
7847 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007848 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007849 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007850 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007851 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007852 return arguments;
7853}
7854
7855
7856// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007857// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007858// extension part has all the parameters and locals of the function on the
7859// stack frame. A function which calls eval with the code to evaluate is then
7860// compiled in this context and called in this context. As this context
7861// replaces the context of the function on the stack frame a new (empty)
7862// function is created as well to be used as the closure for the context.
7863// This function and the context acts as replacements for the function on the
7864// stack frame presenting the same view of the values of parameters and
7865// local variables as if the piece of JavaScript was evaluated at the point
7866// where the function on the stack frame is currently stopped.
7867static Object* Runtime_DebugEvaluate(Arguments args) {
7868 HandleScope scope;
7869
7870 // Check the execution state and decode arguments frame and source to be
7871 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007872 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007873 Object* check_result = Runtime_CheckExecutionState(args);
7874 if (check_result->IsFailure()) return check_result;
7875 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7876 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007877 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7878
7879 // Handle the processing of break.
7880 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007881
7882 // Get the frame where the debugging is performed.
7883 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7884 JavaScriptFrameIterator it(id);
7885 JavaScriptFrame* frame = it.frame();
7886 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7887 Handle<Code> code(function->code());
7888 ScopeInfo<> sinfo(*code);
7889
7890 // Traverse the saved contexts chain to find the active context for the
7891 // selected frame.
7892 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007893 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007894 save = save->prev();
7895 }
7896 ASSERT(save != NULL);
7897 SaveContext savex;
7898 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007899
7900 // Create the (empty) function replacing the function on the stack frame for
7901 // the purpose of evaluating in the context created below. It is important
7902 // that this function does not describe any parameters and local variables
7903 // in the context. If it does then this will cause problems with the lookup
7904 // in Context::Lookup, where context slots for parameters and local variables
7905 // are looked at before the extension object.
7906 Handle<JSFunction> go_between =
7907 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7908 go_between->set_context(function->context());
7909#ifdef DEBUG
7910 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7911 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7912 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7913#endif
7914
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007915 // Materialize the content of the local scope into a JSObject.
7916 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007917
7918 // Allocate a new context for the debug evaluation and set the extension
7919 // object build.
7920 Handle<Context> context =
7921 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007922 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007923 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007924 Handle<Context> frame_context(Context::cast(frame->context()));
7925 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007926 context = CopyWithContextChain(frame_context, context);
7927
7928 // Wrap the evaluation statement in a new function compiled in the newly
7929 // created context. The function has one parameter which has to be called
7930 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007931 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007932 // function(arguments,__source__) {return eval(__source__);}
7933 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007934 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007935 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007936 Handle<String> function_source =
7937 Factory::NewStringFromAscii(Vector<const char>(source_str,
7938 source_str_length));
7939 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007940 Compiler::CompileEval(function_source,
7941 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007942 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007943 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007944 if (boilerplate.is_null()) return Failure::Exception();
7945 Handle<JSFunction> compiled_function =
7946 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7947
7948 // Invoke the result of the compilation to get the evaluation function.
7949 bool has_pending_exception;
7950 Handle<Object> receiver(frame->receiver());
7951 Handle<Object> evaluation_function =
7952 Execution::Call(compiled_function, receiver, 0, NULL,
7953 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007954 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007955
7956 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7957 function_context);
7958
7959 // Invoke the evaluation function and return the result.
7960 const int argc = 2;
7961 Object** argv[argc] = { arguments.location(),
7962 Handle<Object>::cast(source).location() };
7963 Handle<Object> result =
7964 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7965 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007966 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007967
7968 // Skip the global proxy as it has no properties and always delegates to the
7969 // real global object.
7970 if (result->IsJSGlobalProxy()) {
7971 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7972 }
7973
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007974 return *result;
7975}
7976
7977
7978static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7979 HandleScope scope;
7980
7981 // Check the execution state and decode arguments frame and source to be
7982 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007983 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007984 Object* check_result = Runtime_CheckExecutionState(args);
7985 if (check_result->IsFailure()) return check_result;
7986 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007987 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7988
7989 // Handle the processing of break.
7990 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007991
7992 // Enter the top context from before the debugger was invoked.
7993 SaveContext save;
7994 SaveContext* top = &save;
7995 while (top != NULL && *top->context() == *Debug::debug_context()) {
7996 top = top->prev();
7997 }
7998 if (top != NULL) {
7999 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008000 }
8001
8002 // Get the global context now set to the top context from before the
8003 // debugger was invoked.
8004 Handle<Context> context = Top::global_context();
8005
8006 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00008007 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008008 Handle<JSFunction>(Compiler::CompileEval(source,
8009 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008010 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00008011 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008012 if (boilerplate.is_null()) return Failure::Exception();
8013 Handle<JSFunction> compiled_function =
8014 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
8015 context));
8016
8017 // Invoke the result of the compilation to get the evaluation function.
8018 bool has_pending_exception;
8019 Handle<Object> receiver = Top::global();
8020 Handle<Object> result =
8021 Execution::Call(compiled_function, receiver, 0, NULL,
8022 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008023 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008024 return *result;
8025}
8026
8027
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008028static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
8029 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00008030 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008031
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008032 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008033 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008034
8035 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008036 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00008037 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
8038 // Get the script wrapper in a local handle before calling GetScriptWrapper,
8039 // because using
8040 // instances->set(i, *GetScriptWrapper(script))
8041 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
8042 // already have deferenced the instances handle.
8043 Handle<JSValue> wrapper = GetScriptWrapper(script);
8044 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045 }
8046
8047 // Return result as a JS array.
8048 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
8049 Handle<JSArray>::cast(result)->SetContent(*instances);
8050 return *result;
8051}
8052
8053
8054// Helper function used by Runtime_DebugReferencedBy below.
8055static int DebugReferencedBy(JSObject* target,
8056 Object* instance_filter, int max_references,
8057 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008058 JSFunction* arguments_function) {
8059 NoHandleAllocation ha;
8060 AssertNoAllocation no_alloc;
8061
8062 // Iterate the heap.
8063 int count = 0;
8064 JSObject* last = NULL;
8065 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008066 HeapObject* heap_obj = NULL;
8067 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008068 (max_references == 0 || count < max_references)) {
8069 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008070 if (heap_obj->IsJSObject()) {
8071 // Skip context extension objects and argument arrays as these are
8072 // checked in the context of functions using them.
8073 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008074 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008075 obj->map()->constructor() == arguments_function) {
8076 continue;
8077 }
8078
8079 // Check if the JS object has a reference to the object looked for.
8080 if (obj->ReferencesObject(target)) {
8081 // Check instance filter if supplied. This is normally used to avoid
8082 // references from mirror objects (see Runtime_IsInPrototypeChain).
8083 if (!instance_filter->IsUndefined()) {
8084 Object* V = obj;
8085 while (true) {
8086 Object* prototype = V->GetPrototype();
8087 if (prototype->IsNull()) {
8088 break;
8089 }
8090 if (instance_filter == prototype) {
8091 obj = NULL; // Don't add this object.
8092 break;
8093 }
8094 V = prototype;
8095 }
8096 }
8097
8098 if (obj != NULL) {
8099 // Valid reference found add to instance array if supplied an update
8100 // count.
8101 if (instances != NULL && count < instances_size) {
8102 instances->set(count, obj);
8103 }
8104 last = obj;
8105 count++;
8106 }
8107 }
8108 }
8109 }
8110
8111 // Check for circular reference only. This can happen when the object is only
8112 // referenced from mirrors and has a circular reference in which case the
8113 // object is not really alive and would have been garbage collected if not
8114 // referenced from the mirror.
8115 if (count == 1 && last == target) {
8116 count = 0;
8117 }
8118
8119 // Return the number of referencing objects found.
8120 return count;
8121}
8122
8123
8124// Scan the heap for objects with direct references to an object
8125// args[0]: the object to find references to
8126// args[1]: constructor function for instances to exclude (Mirror)
8127// args[2]: the the maximum number of objects to return
8128static Object* Runtime_DebugReferencedBy(Arguments args) {
8129 ASSERT(args.length() == 3);
8130
8131 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008132 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008133
8134 // Check parameters.
8135 CONVERT_CHECKED(JSObject, target, args[0]);
8136 Object* instance_filter = args[1];
8137 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
8138 instance_filter->IsJSObject());
8139 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
8140 RUNTIME_ASSERT(max_references >= 0);
8141
8142 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008143 JSObject* arguments_boilerplate =
8144 Top::context()->global_context()->arguments_boilerplate();
8145 JSFunction* arguments_function =
8146 JSFunction::cast(arguments_boilerplate->map()->constructor());
8147
8148 // Get the number of referencing objects.
8149 int count;
8150 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00008151 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008152
8153 // Allocate an array to hold the result.
8154 Object* object = Heap::AllocateFixedArray(count);
8155 if (object->IsFailure()) return object;
8156 FixedArray* instances = FixedArray::cast(object);
8157
8158 // Fill the referencing objects.
8159 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00008160 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008161
8162 // Return result as JS array.
8163 Object* result =
8164 Heap::AllocateJSObject(
8165 Top::context()->global_context()->array_function());
8166 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
8167 return result;
8168}
8169
8170
8171// Helper function used by Runtime_DebugConstructedBy below.
8172static int DebugConstructedBy(JSFunction* constructor, int max_references,
8173 FixedArray* instances, int instances_size) {
8174 AssertNoAllocation no_alloc;
8175
8176 // Iterate the heap.
8177 int count = 0;
8178 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008179 HeapObject* heap_obj = NULL;
8180 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008181 (max_references == 0 || count < max_references)) {
8182 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008183 if (heap_obj->IsJSObject()) {
8184 JSObject* obj = JSObject::cast(heap_obj);
8185 if (obj->map()->constructor() == constructor) {
8186 // Valid reference found add to instance array if supplied an update
8187 // count.
8188 if (instances != NULL && count < instances_size) {
8189 instances->set(count, obj);
8190 }
8191 count++;
8192 }
8193 }
8194 }
8195
8196 // Return the number of referencing objects found.
8197 return count;
8198}
8199
8200
8201// Scan the heap for objects constructed by a specific function.
8202// args[0]: the constructor to find instances of
8203// args[1]: the the maximum number of objects to return
8204static Object* Runtime_DebugConstructedBy(Arguments args) {
8205 ASSERT(args.length() == 2);
8206
8207 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008208 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008209
8210 // Check parameters.
8211 CONVERT_CHECKED(JSFunction, constructor, args[0]);
8212 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
8213 RUNTIME_ASSERT(max_references >= 0);
8214
8215 // Get the number of referencing objects.
8216 int count;
8217 count = DebugConstructedBy(constructor, max_references, NULL, 0);
8218
8219 // Allocate an array to hold the result.
8220 Object* object = Heap::AllocateFixedArray(count);
8221 if (object->IsFailure()) return object;
8222 FixedArray* instances = FixedArray::cast(object);
8223
8224 // Fill the referencing objects.
8225 count = DebugConstructedBy(constructor, max_references, instances, count);
8226
8227 // Return result as JS array.
8228 Object* result =
8229 Heap::AllocateJSObject(
8230 Top::context()->global_context()->array_function());
8231 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
8232 return result;
8233}
8234
8235
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008236// Find the effective prototype object as returned by __proto__.
8237// args[0]: the object to find the prototype for.
8238static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008239 ASSERT(args.length() == 1);
8240
8241 CONVERT_CHECKED(JSObject, obj, args[0]);
8242
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008243 // Use the __proto__ accessor.
8244 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008245}
8246
8247
8248static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008249 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008250 CPU::DebugBreak();
8251 return Heap::undefined_value();
8252}
8253
8254
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008255static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008256#ifdef DEBUG
8257 HandleScope scope;
8258 ASSERT(args.length() == 1);
8259 // Get the function and make sure it is compiled.
8260 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008261 Handle<SharedFunctionInfo> shared(func->shared());
8262 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008263 return Failure::Exception();
8264 }
8265 func->code()->PrintLn();
8266#endif // DEBUG
8267 return Heap::undefined_value();
8268}
ager@chromium.org9085a012009-05-11 19:22:57 +00008269
8270
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008271static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
8272#ifdef DEBUG
8273 HandleScope scope;
8274 ASSERT(args.length() == 1);
8275 // Get the function and make sure it is compiled.
8276 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008277 Handle<SharedFunctionInfo> shared(func->shared());
8278 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008279 return Failure::Exception();
8280 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008281 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008282#endif // DEBUG
8283 return Heap::undefined_value();
8284}
8285
8286
ager@chromium.org9085a012009-05-11 19:22:57 +00008287static Object* Runtime_FunctionGetInferredName(Arguments args) {
8288 NoHandleAllocation ha;
8289 ASSERT(args.length() == 1);
8290
8291 CONVERT_CHECKED(JSFunction, f, args[0]);
8292 return f->shared()->inferred_name();
8293}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008294
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008295
8296static int FindSharedFunctionInfosForScript(Script* script,
8297 FixedArray* buffer) {
8298 AssertNoAllocation no_allocations;
8299
8300 int counter = 0;
8301 int buffer_size = buffer->length();
8302 HeapIterator iterator;
8303 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
8304 ASSERT(obj != NULL);
8305 if (!obj->IsSharedFunctionInfo()) {
8306 continue;
8307 }
8308 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
8309 if (shared->script() != script) {
8310 continue;
8311 }
8312 if (counter < buffer_size) {
8313 buffer->set(counter, shared);
8314 }
8315 counter++;
8316 }
8317 return counter;
8318}
8319
8320// For a script finds all SharedFunctionInfo's in the heap that points
8321// to this script. Returns JSArray of SharedFunctionInfo wrapped
8322// in OpaqueReferences.
8323static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
8324 Arguments args) {
8325 ASSERT(args.length() == 1);
8326 HandleScope scope;
8327 CONVERT_CHECKED(JSValue, script_value, args[0]);
8328
8329 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
8330
8331 const int kBufferSize = 32;
8332
8333 Handle<FixedArray> array;
8334 array = Factory::NewFixedArray(kBufferSize);
8335 int number = FindSharedFunctionInfosForScript(*script, *array);
8336 if (number > kBufferSize) {
8337 array = Factory::NewFixedArray(number);
8338 FindSharedFunctionInfosForScript(*script, *array);
8339 }
8340
8341 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
8342 result->set_length(Smi::FromInt(number));
8343
8344 LiveEdit::WrapSharedFunctionInfos(result);
8345
8346 return *result;
8347}
8348
8349// For a script calculates compilation information about all its functions.
8350// The script source is explicitly specified by the second argument.
8351// The source of the actual script is not used, however it is important that
8352// all generated code keeps references to this particular instance of script.
8353// Returns a JSArray of compilation infos. The array is ordered so that
8354// each function with all its descendant is always stored in a continues range
8355// with the function itself going first. The root function is a script function.
8356static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
8357 ASSERT(args.length() == 2);
8358 HandleScope scope;
8359 CONVERT_CHECKED(JSValue, script, args[0]);
8360 CONVERT_ARG_CHECKED(String, source, 1);
8361 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
8362
8363 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
8364
8365 if (Top::has_pending_exception()) {
8366 return Failure::Exception();
8367 }
8368
8369 return result;
8370}
8371
8372// Changes the source of the script to a new_source and creates a new
8373// script representing the old version of the script source.
8374static Object* Runtime_LiveEditReplaceScript(Arguments args) {
8375 ASSERT(args.length() == 3);
8376 HandleScope scope;
8377 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
8378 CONVERT_ARG_CHECKED(String, new_source, 1);
8379 CONVERT_ARG_CHECKED(String, old_script_name, 2);
8380 Handle<Script> original_script =
8381 Handle<Script>(Script::cast(original_script_value->value()));
8382
8383 Handle<String> original_source(String::cast(original_script->source()));
8384
8385 original_script->set_source(*new_source);
8386 Handle<Script> old_script = Factory::NewScript(original_source);
8387 old_script->set_name(*old_script_name);
8388 old_script->set_line_offset(original_script->line_offset());
8389 old_script->set_column_offset(original_script->column_offset());
8390 old_script->set_data(original_script->data());
8391 old_script->set_type(original_script->type());
8392 old_script->set_context_data(original_script->context_data());
8393 old_script->set_compilation_type(original_script->compilation_type());
8394 old_script->set_eval_from_shared(original_script->eval_from_shared());
8395 old_script->set_eval_from_instructions_offset(
8396 original_script->eval_from_instructions_offset());
8397
8398
8399 Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
8400
8401 return *(GetScriptWrapper(old_script));
8402}
8403
8404// Replaces code of SharedFunctionInfo with a new one.
8405static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
8406 ASSERT(args.length() == 2);
8407 HandleScope scope;
8408 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
8409 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
8410
8411 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
8412
8413 return Heap::undefined_value();
8414}
8415
8416// Connects SharedFunctionInfo to another script.
8417static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
8418 ASSERT(args.length() == 2);
8419 HandleScope scope;
8420 CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
8421 CONVERT_ARG_CHECKED(JSValue, script_value, 1);
8422 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
8423
8424 LiveEdit::RelinkFunctionToScript(shared_info_array, script);
8425
8426 return Heap::undefined_value();
8427}
8428
8429// Updates positions of a shared function info (first parameter) according
8430// to script source change. Text change is described in second parameter as
8431// array of groups of 3 numbers:
8432// (change_begin, change_end, change_end_new_position).
8433// Each group describes a change in text; groups are sorted by change_begin.
8434static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
8435 ASSERT(args.length() == 2);
8436 HandleScope scope;
8437 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
8438 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
8439
8440 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
8441
8442 return Heap::undefined_value();
8443}
8444
8445
8446static LiveEdit::FunctionPatchabilityStatus FindFunctionCodeOnStacks(
8447 Handle<SharedFunctionInfo> shared) {
8448 // TODO(635): check all threads, not only the current one.
8449 for (StackFrameIterator it; !it.done(); it.Advance()) {
8450 StackFrame* frame = it.frame();
8451 if (frame->code() == shared->code()) {
8452 return LiveEdit::FUNCTION_BLOCKED_ON_STACK;
8453 }
8454 }
8455 return LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH;
8456}
8457
8458// For array of SharedFunctionInfo's (each wrapped in JSValue)
8459// checks that none of them have activations on stacks (of any thread).
8460// Returns array of the same length with corresponding results of
8461// LiveEdit::FunctionPatchabilityStatus type.
8462static Object* Runtime_LiveEditCheckStackActivations(Arguments args) {
8463 ASSERT(args.length() == 1);
8464 HandleScope scope;
8465 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
8466
8467
8468 int len = Smi::cast(shared_array->length())->value();
8469 Handle<JSArray> result = Factory::NewJSArray(len);
8470
8471 for (int i = 0; i < len; i++) {
8472 JSValue* wrapper = JSValue::cast(shared_array->GetElement(i));
8473 Handle<SharedFunctionInfo> shared(
8474 SharedFunctionInfo::cast(wrapper->value()));
8475 LiveEdit::FunctionPatchabilityStatus check_res =
8476 FindFunctionCodeOnStacks(shared);
8477 SetElement(result, i, Handle<Smi>(Smi::FromInt(check_res)));
8478 }
8479
8480 return *result;
8481}
8482
8483
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008484#endif // ENABLE_DEBUGGER_SUPPORT
8485
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008486#ifdef ENABLE_LOGGING_AND_PROFILING
8487
8488static Object* Runtime_ProfilerResume(Arguments args) {
8489 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00008490 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008491
8492 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00008493 CONVERT_CHECKED(Smi, smi_tag, args[1]);
8494 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008495 return Heap::undefined_value();
8496}
8497
8498
8499static Object* Runtime_ProfilerPause(Arguments args) {
8500 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00008501 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008502
8503 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00008504 CONVERT_CHECKED(Smi, smi_tag, args[1]);
8505 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008506 return Heap::undefined_value();
8507}
8508
8509#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008510
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008511// Finds the script object from the script data. NOTE: This operation uses
8512// heap traversal to find the function generated for the source position
8513// for the requested break point. For lazily compiled functions several heap
8514// traversals might be required rendering this operation as a rather slow
8515// operation. However for setting break points which is normally done through
8516// some kind of user interaction the performance is not crucial.
8517static Handle<Object> Runtime_GetScriptFromScriptName(
8518 Handle<String> script_name) {
8519 // Scan the heap for Script objects to find the script with the requested
8520 // script data.
8521 Handle<Script> script;
8522 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008523 HeapObject* obj = NULL;
8524 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008525 // If a script is found check if it has the script data requested.
8526 if (obj->IsScript()) {
8527 if (Script::cast(obj)->name()->IsString()) {
8528 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
8529 script = Handle<Script>(Script::cast(obj));
8530 }
8531 }
8532 }
8533 }
8534
8535 // If no script with the requested script data is found return undefined.
8536 if (script.is_null()) return Factory::undefined_value();
8537
8538 // Return the script found.
8539 return GetScriptWrapper(script);
8540}
8541
8542
8543// Get the script object from script data. NOTE: Regarding performance
8544// see the NOTE for GetScriptFromScriptData.
8545// args[0]: script data for the script to find the source for
8546static Object* Runtime_GetScript(Arguments args) {
8547 HandleScope scope;
8548
8549 ASSERT(args.length() == 1);
8550
8551 CONVERT_CHECKED(String, script_name, args[0]);
8552
8553 // Find the requested script.
8554 Handle<Object> result =
8555 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
8556 return *result;
8557}
8558
8559
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008560// Determines whether the given stack frame should be displayed in
8561// a stack trace. The caller is the error constructor that asked
8562// for the stack trace to be collected. The first time a construct
8563// call to this function is encountered it is skipped. The seen_caller
8564// in/out parameter is used to remember if the caller has been seen
8565// yet.
8566static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
8567 bool* seen_caller) {
8568 // Only display JS frames.
8569 if (!raw_frame->is_java_script())
8570 return false;
8571 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
8572 Object* raw_fun = frame->function();
8573 // Not sure when this can happen but skip it just in case.
8574 if (!raw_fun->IsJSFunction())
8575 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008576 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008577 *seen_caller = true;
8578 return false;
8579 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008580 // Skip all frames until we've seen the caller. Also, skip the most
8581 // obvious builtin calls. Some builtin calls (such as Number.ADD
8582 // which is invoked using 'call') are very difficult to recognize
8583 // so we're leaving them in for now.
8584 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008585}
8586
8587
8588// Collect the raw data for a stack trace. Returns an array of three
8589// element segments each containing a receiver, function and native
8590// code offset.
8591static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008592 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008593 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008594 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
8595
8596 HandleScope scope;
8597
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008598 limit = Max(limit, 0); // Ensure that limit is not negative.
8599 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008600 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008601
8602 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008603 // If the caller parameter is a function we skip frames until we're
8604 // under it before starting to collect.
8605 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008606 int cursor = 0;
8607 int frames_seen = 0;
8608 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008609 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008610 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008611 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008612 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008613 Object* recv = frame->receiver();
8614 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008615 Address pc = frame->pc();
8616 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008617 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008618 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008619 if (cursor + 2 < elements->length()) {
8620 elements->set(cursor++, recv);
8621 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008622 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008623 } else {
8624 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008625 Handle<Object> recv_handle(recv);
8626 Handle<Object> fun_handle(fun);
8627 SetElement(result, cursor++, recv_handle);
8628 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008629 SetElement(result, cursor++, Handle<Smi>(offset));
8630 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008631 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008632 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008633 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008634
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008635 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008636 return *result;
8637}
8638
8639
ager@chromium.org3811b432009-10-28 14:53:37 +00008640// Returns V8 version as a string.
8641static Object* Runtime_GetV8Version(Arguments args) {
8642 ASSERT_EQ(args.length(), 0);
8643
8644 NoHandleAllocation ha;
8645
8646 const char* version_string = v8::V8::GetVersion();
8647
8648 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
8649}
8650
8651
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008652static Object* Runtime_Abort(Arguments args) {
8653 ASSERT(args.length() == 2);
8654 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
8655 Smi::cast(args[1])->value());
8656 Top::PrintStack();
8657 OS::Abort();
8658 UNREACHABLE();
8659 return NULL;
8660}
8661
8662
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008663static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
8664 ASSERT(args.length() == 0);
8665 HandleScope::DeleteExtensions();
8666 return Heap::undefined_value();
8667}
8668
8669
kasper.lund44510672008-07-25 07:37:58 +00008670#ifdef DEBUG
8671// ListNatives is ONLY used by the fuzz-natives.js in debug mode
8672// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008673static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008674 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008675 HandleScope scope;
8676 Handle<JSArray> result = Factory::NewJSArray(0);
8677 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00008678#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008679 { \
8680 HandleScope inner; \
8681 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008682 Factory::NewStringFromAscii( \
8683 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008684 Handle<JSArray> pair = Factory::NewJSArray(0); \
8685 SetElement(pair, 0, name); \
8686 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
8687 SetElement(result, index++, pair); \
8688 }
8689 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
8690#undef ADD_ENTRY
8691 return *result;
8692}
kasper.lund44510672008-07-25 07:37:58 +00008693#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008694
8695
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008696static Object* Runtime_Log(Arguments args) {
8697 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00008698 CONVERT_CHECKED(String, format, args[0]);
8699 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008700 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008701 Logger::LogRuntime(chars, elms);
8702 return Heap::undefined_value();
8703}
8704
8705
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008706static Object* Runtime_IS_VAR(Arguments args) {
8707 UNREACHABLE(); // implemented as macro in the parser
8708 return NULL;
8709}
8710
8711
8712// ----------------------------------------------------------------------------
8713// Implementation of Runtime
8714
ager@chromium.orga1645e22009-09-09 19:27:10 +00008715#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008716 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00008717 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008718
8719static Runtime::Function Runtime_functions[] = {
8720 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008721 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008722};
8723
8724#undef F
8725
8726
8727Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
8728 ASSERT(0 <= fid && fid < kNofFunctions);
8729 return &Runtime_functions[fid];
8730}
8731
8732
8733Runtime::Function* Runtime::FunctionForName(const char* name) {
8734 for (Function* f = Runtime_functions; f->name != NULL; f++) {
8735 if (strcmp(f->name, name) == 0) {
8736 return f;
8737 }
8738 }
8739 return NULL;
8740}
8741
8742
8743void Runtime::PerformGC(Object* result) {
8744 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008745 if (failure->IsRetryAfterGC()) {
8746 // Try to do a garbage collection; ignore it if it fails. The C
8747 // entry stub will throw an out-of-memory exception in that case.
8748 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
8749 } else {
8750 // Handle last resort GC and make sure to allow future allocations
8751 // to grow the heap without causing GCs (if possible).
8752 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008753 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008754 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008755}
8756
8757
8758} } // namespace v8::internal