blob: 6459aa78d12711dce02b0506b10d957214986eaf [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
35#include "compiler.h"
36#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000037#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038#include "debug.h"
39#include "execution.h"
40#include "jsregexp.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000041#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042#include "platform.h"
43#include "runtime.h"
44#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000045#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000046#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000047#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048
kasperl@chromium.org71affb52009-05-26 05:44:31 +000049namespace v8 {
50namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051
52
ager@chromium.org3e875802009-06-29 08:26:34 +000053#define RUNTIME_ASSERT(value) \
54 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000055
56// Cast the given object to a value of the specified type and store
57// it in a variable with the given name. If the object is not of the
58// expected type call IllegalOperation and return.
59#define CONVERT_CHECKED(Type, name, obj) \
60 RUNTIME_ASSERT(obj->Is##Type()); \
61 Type* name = Type::cast(obj);
62
63#define CONVERT_ARG_CHECKED(Type, name, index) \
64 RUNTIME_ASSERT(args[index]->Is##Type()); \
65 Handle<Type> name = args.at<Type>(index);
66
kasper.lundbd3ec4e2008-07-09 11:06:54 +000067// Cast the given object to a boolean and store it in a variable with
68// the given name. If the object is not a boolean call IllegalOperation
69// and return.
70#define CONVERT_BOOLEAN_CHECKED(name, obj) \
71 RUNTIME_ASSERT(obj->IsBoolean()); \
72 bool name = (obj)->IsTrue();
73
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000074// Cast the given object to a Smi and store its value in an int variable
75// with the given name. If the object is not a Smi call IllegalOperation
76// and return.
77#define CONVERT_SMI_CHECKED(name, obj) \
78 RUNTIME_ASSERT(obj->IsSmi()); \
79 int name = Smi::cast(obj)->value();
80
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000081// Cast the given object to a double and store it in a variable with
82// the given name. If the object is not a number (as opposed to
83// the number not-a-number) call IllegalOperation and return.
84#define CONVERT_DOUBLE_CHECKED(name, obj) \
85 RUNTIME_ASSERT(obj->IsNumber()); \
86 double name = (obj)->Number();
87
88// Call the specified converter on the object *comand store the result in
89// a variable of the specified type with the given name. If the
90// object is not a Number call IllegalOperation and return.
91#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
92 RUNTIME_ASSERT(obj->IsNumber()); \
93 type name = NumberTo##Type(obj);
94
95// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000096static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097
98
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000099static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
100 StackLimitCheck check;
101 if (check.HasOverflowed()) return Top::StackOverflow();
102
103 Object* result = Heap::CopyJSObject(boilerplate);
104 if (result->IsFailure()) return result;
105 JSObject* copy = JSObject::cast(result);
106
107 // Deep copy local properties.
108 if (copy->HasFastProperties()) {
109 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000110 for (int i = 0; i < properties->length(); i++) {
111 Object* value = properties->get(i);
112 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000113 JSObject* js_object = JSObject::cast(value);
114 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000115 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000116 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 }
118 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000119 int nof = copy->map()->inobject_properties();
120 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000121 Object* value = copy->InObjectPropertyAt(i);
122 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000123 JSObject* js_object = JSObject::cast(value);
124 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000125 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000126 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 }
128 }
129 } else {
130 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
131 if (result->IsFailure()) return result;
132 FixedArray* names = FixedArray::cast(result);
133 copy->GetLocalPropertyNames(names, 0);
134 for (int i = 0; i < names->length(); i++) {
135 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000136 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000137 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 // Only deep copy fields from the object literal expression.
140 // In particular, don't try to copy the length attribute of
141 // an array.
142 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000143 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000144 ASSERT(!value->IsFailure());
145 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000146 JSObject* js_object = JSObject::cast(value);
147 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000148 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000149 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
151 }
152 }
153 }
154
155 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000156 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000157 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 switch (copy->GetElementsKind()) {
159 case JSObject::FAST_ELEMENTS: {
160 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000161 for (int i = 0; i < elements->length(); i++) {
162 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000163 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000164 JSObject* js_object = JSObject::cast(value);
165 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000166 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000167 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 }
169 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000170 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 case JSObject::DICTIONARY_ELEMENTS: {
173 NumberDictionary* element_dictionary = copy->element_dictionary();
174 int capacity = element_dictionary->Capacity();
175 for (int i = 0; i < capacity; i++) {
176 Object* k = element_dictionary->KeyAt(i);
177 if (element_dictionary->IsKey(k)) {
178 Object* value = element_dictionary->ValueAt(i);
179 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000180 JSObject* js_object = JSObject::cast(value);
181 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000182 if (result->IsFailure()) return result;
183 element_dictionary->ValueAtPut(i, result);
184 }
185 }
186 }
187 break;
188 }
189 default:
190 UNREACHABLE();
191 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000192 }
193 return copy;
194}
195
196
197static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
198 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
199 return DeepCopyBoilerplate(boilerplate);
200}
201
202
203static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000205 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206}
207
208
ager@chromium.org236ad962008-09-25 09:45:57 +0000209static Handle<Map> ComputeObjectLiteralMap(
210 Handle<Context> context,
211 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000212 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000213 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000214 if (FLAG_canonicalize_object_literal_maps) {
215 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 int number_of_symbol_keys = 0;
217 while ((number_of_symbol_keys < number_of_properties) &&
218 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
219 number_of_symbol_keys++;
220 }
221 // Based on the number of prefix symbols key we decide whether
222 // to use the map cache in the global context.
223 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000224 if ((number_of_symbol_keys == number_of_properties) &&
225 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000226 // Create the fixed array with the key.
227 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
228 for (int i = 0; i < number_of_symbol_keys; i++) {
229 keys->set(i, constant_properties->get(i*2));
230 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000231 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000232 return Factory::ObjectLiteralMapFromCache(context, keys);
233 }
234 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000235 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000236 return Factory::CopyMap(
237 Handle<Map>(context->object_function()->initial_map()),
238 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000239}
240
241
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000242static Handle<Object> CreateLiteralBoilerplate(
243 Handle<FixedArray> literals,
244 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000245
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000246
247static Handle<Object> CreateObjectLiteralBoilerplate(
248 Handle<FixedArray> literals,
249 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000250 // Get the global context from the literals array. This is the
251 // context in which the function was created and we use the object
252 // function from this context to create the object literal. We do
253 // not use the object function from the current global context
254 // because this might be the object function from another context
255 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000256 Handle<Context> context =
257 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
258
259 bool is_result_from_cache;
260 Handle<Map> map = ComputeObjectLiteralMap(context,
261 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000262 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000263
ager@chromium.org236ad962008-09-25 09:45:57 +0000264 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000265 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000268 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000269 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000270 for (int index = 0; index < length; index +=2) {
271 Handle<Object> key(constant_properties->get(index+0));
272 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000273 if (value->IsFixedArray()) {
274 // The value contains the constant_properties of a
275 // simple object literal.
276 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
277 value = CreateLiteralBoilerplate(literals, array);
278 if (value.is_null()) return value;
279 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000280 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000281 uint32_t element_index = 0;
282 if (key->IsSymbol()) {
283 // If key is a symbol it is not an array element.
284 Handle<String> name(String::cast(*key));
285 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000286 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000287 } else if (Array::IndexFromObject(*key, &element_index)) {
288 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000289 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290 } else {
291 // Non-uint32 number.
292 ASSERT(key->IsNumber());
293 double num = key->Number();
294 char arr[100];
295 Vector<char> buffer(arr, ARRAY_SIZE(arr));
296 const char* str = DoubleToCString(num, buffer);
297 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000298 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000299 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000300 // If setting the property on the boilerplate throws an
301 // exception, the exception is converted to an empty handle in
302 // the handle based operations. In that case, we need to
303 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000304 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000305 }
306 }
307
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000308 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000309}
310
311
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000312static Handle<Object> CreateArrayLiteralBoilerplate(
313 Handle<FixedArray> literals,
314 Handle<FixedArray> elements) {
315 // Create the JSArray.
316 Handle<JSFunction> constructor(
317 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
318 Handle<Object> object = Factory::NewJSObject(constructor);
319
320 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
321
322 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
323 for (int i = 0; i < content->length(); i++) {
324 if (content->get(i)->IsFixedArray()) {
325 // The value contains the constant_properties of a
326 // simple object literal.
327 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
328 Handle<Object> result =
329 CreateLiteralBoilerplate(literals, fa);
330 if (result.is_null()) return result;
331 content->set(i, *result);
332 }
333 }
334
335 // Set the elements.
336 Handle<JSArray>::cast(object)->SetContent(*content);
337 return object;
338}
339
340
341static Handle<Object> CreateLiteralBoilerplate(
342 Handle<FixedArray> literals,
343 Handle<FixedArray> array) {
344 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
345 switch (CompileTimeValue::GetType(array)) {
346 case CompileTimeValue::OBJECT_LITERAL:
347 return CreateObjectLiteralBoilerplate(literals, elements);
348 case CompileTimeValue::ARRAY_LITERAL:
349 return CreateArrayLiteralBoilerplate(literals, elements);
350 default:
351 UNREACHABLE();
352 return Handle<Object>::null();
353 }
354}
355
356
357static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
358 HandleScope scope;
359 ASSERT(args.length() == 3);
360 // Copy the arguments.
361 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
362 CONVERT_SMI_CHECKED(literals_index, args[1]);
363 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
364
365 Handle<Object> result =
366 CreateObjectLiteralBoilerplate(literals, constant_properties);
367
368 if (result.is_null()) return Failure::Exception();
369
370 // Update the functions literal and return the boilerplate.
371 literals->set(literals_index, *result);
372
373 return *result;
374}
375
376
377static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000378 // Takes a FixedArray of elements containing the literal elements of
379 // the array literal and produces JSArray with those elements.
380 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000381 // which contains the context from which to get the Array function
382 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000383 HandleScope scope;
384 ASSERT(args.length() == 3);
385 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
386 CONVERT_SMI_CHECKED(literals_index, args[1]);
387 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000388
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000389 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
390 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000392 // Update the functions literal and return the boilerplate.
393 literals->set(literals_index, *object);
394 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000395}
396
397
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000398static Object* Runtime_CreateObjectLiteral(Arguments args) {
399 HandleScope scope;
400 ASSERT(args.length() == 3);
401 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
402 CONVERT_SMI_CHECKED(literals_index, args[1]);
403 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
404
405 // Check if boilerplate exists. If not, create it first.
406 Handle<Object> boilerplate(literals->get(literals_index));
407 if (*boilerplate == Heap::undefined_value()) {
408 boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
409 if (boilerplate.is_null()) return Failure::Exception();
410 // Update the functions literal and return the boilerplate.
411 literals->set(literals_index, *boilerplate);
412 }
413 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
414}
415
416
417static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
418 HandleScope scope;
419 ASSERT(args.length() == 3);
420 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
421 CONVERT_SMI_CHECKED(literals_index, args[1]);
422 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
423
424 // Check if boilerplate exists. If not, create it first.
425 Handle<Object> boilerplate(literals->get(literals_index));
426 if (*boilerplate == Heap::undefined_value()) {
427 boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
428 if (boilerplate.is_null()) return Failure::Exception();
429 // Update the functions literal and return the boilerplate.
430 literals->set(literals_index, *boilerplate);
431 }
432 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
433}
434
435
436static Object* Runtime_CreateArrayLiteral(Arguments args) {
437 HandleScope scope;
438 ASSERT(args.length() == 3);
439 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
440 CONVERT_SMI_CHECKED(literals_index, args[1]);
441 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
442
443 // Check if boilerplate exists. If not, create it first.
444 Handle<Object> boilerplate(literals->get(literals_index));
445 if (*boilerplate == Heap::undefined_value()) {
446 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
447 if (boilerplate.is_null()) return Failure::Exception();
448 // Update the functions literal and return the boilerplate.
449 literals->set(literals_index, *boilerplate);
450 }
451 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
452}
453
454
455static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
456 HandleScope scope;
457 ASSERT(args.length() == 3);
458 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
459 CONVERT_SMI_CHECKED(literals_index, args[1]);
460 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
461
462 // Check if boilerplate exists. If not, create it first.
463 Handle<Object> boilerplate(literals->get(literals_index));
464 if (*boilerplate == Heap::undefined_value()) {
465 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
466 if (boilerplate.is_null()) return Failure::Exception();
467 // Update the functions literal and return the boilerplate.
468 literals->set(literals_index, *boilerplate);
469 }
470 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
471}
472
473
ager@chromium.org32912102009-01-16 10:38:43 +0000474static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
475 ASSERT(args.length() == 2);
476 CONVERT_CHECKED(String, key, args[0]);
477 Object* value = args[1];
478 // Create a catch context extension object.
479 JSFunction* constructor =
480 Top::context()->global_context()->context_extension_function();
481 Object* object = Heap::AllocateJSObject(constructor);
482 if (object->IsFailure()) return object;
483 // Assign the exception value to the catch variable and make sure
484 // that the catch variable is DontDelete.
485 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
486 if (value->IsFailure()) return value;
487 return object;
488}
489
490
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000491static Object* Runtime_ClassOf(Arguments args) {
492 NoHandleAllocation ha;
493 ASSERT(args.length() == 1);
494 Object* obj = args[0];
495 if (!obj->IsJSObject()) return Heap::null_value();
496 return JSObject::cast(obj)->class_name();
497}
498
ager@chromium.org7c537e22008-10-16 08:43:32 +0000499
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000500static Object* Runtime_IsInPrototypeChain(Arguments args) {
501 NoHandleAllocation ha;
502 ASSERT(args.length() == 2);
503 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
504 Object* O = args[0];
505 Object* V = args[1];
506 while (true) {
507 Object* prototype = V->GetPrototype();
508 if (prototype->IsNull()) return Heap::false_value();
509 if (O == prototype) return Heap::true_value();
510 V = prototype;
511 }
512}
513
514
ager@chromium.org9085a012009-05-11 19:22:57 +0000515// Inserts an object as the hidden prototype of another object.
516static Object* Runtime_SetHiddenPrototype(Arguments args) {
517 NoHandleAllocation ha;
518 ASSERT(args.length() == 2);
519 CONVERT_CHECKED(JSObject, jsobject, args[0]);
520 CONVERT_CHECKED(JSObject, proto, args[1]);
521
522 // Sanity checks. The old prototype (that we are replacing) could
523 // theoretically be null, but if it is not null then check that we
524 // didn't already install a hidden prototype here.
525 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
526 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
527 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
528
529 // Allocate up front before we start altering state in case we get a GC.
530 Object* map_or_failure = proto->map()->CopyDropTransitions();
531 if (map_or_failure->IsFailure()) return map_or_failure;
532 Map* new_proto_map = Map::cast(map_or_failure);
533
534 map_or_failure = jsobject->map()->CopyDropTransitions();
535 if (map_or_failure->IsFailure()) return map_or_failure;
536 Map* new_map = Map::cast(map_or_failure);
537
538 // Set proto's prototype to be the old prototype of the object.
539 new_proto_map->set_prototype(jsobject->GetPrototype());
540 proto->set_map(new_proto_map);
541 new_proto_map->set_is_hidden_prototype();
542
543 // Set the object's prototype to proto.
544 new_map->set_prototype(proto);
545 jsobject->set_map(new_map);
546
547 return Heap::undefined_value();
548}
549
550
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551static Object* Runtime_IsConstructCall(Arguments args) {
552 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000553 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000554 JavaScriptFrameIterator it;
555 return Heap::ToBoolean(it.frame()->IsConstructor());
556}
557
558
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000559// Recursively traverses hidden prototypes if property is not found
560static void GetOwnPropertyImplementation(JSObject* obj,
561 String* name,
562 LookupResult* result) {
563 obj->LocalLookupRealNamedProperty(name, result);
564
565 if (!result->IsProperty()) {
566 Object* proto = obj->GetPrototype();
567 if (proto->IsJSObject() &&
568 JSObject::cast(proto)->map()->is_hidden_prototype())
569 GetOwnPropertyImplementation(JSObject::cast(proto),
570 name, result);
571 }
572}
573
574
575// Returns an array with the property description:
576// if args[1] is not a property on args[0]
577// returns undefined
578// if args[1] is a data property on args[0]
579// [false, value, Writeable, Enumerable, Configurable]
580// if args[1] is an accessor on args[0]
581// [true, GetFunction, SetFunction, Enumerable, Configurable]
582static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000583 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000584 HandleScope scope;
585 Handle<FixedArray> elms = Factory::NewFixedArray(5);
586 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
587 LookupResult result;
588 CONVERT_CHECKED(JSObject, obj, args[0]);
589 CONVERT_CHECKED(String, name, args[1]);
590
591 // Use recursive implementation to also traverse hidden prototypes
592 GetOwnPropertyImplementation(obj, name, &result);
593
594 if (!result.IsProperty())
595 return Heap::undefined_value();
596
597 if (result.type() == CALLBACKS) {
598 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000599 if (structure->IsProxy() || structure->IsAccessorInfo()) {
600 // Property that is internally implemented as a callback or
601 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000602 Object* value = obj->GetPropertyWithCallback(
603 obj, structure, name, result.holder());
604 elms->set(0, Heap::false_value());
605 elms->set(1, value);
606 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
607 } else if (structure->IsFixedArray()) {
608 // __defineGetter__/__defineSetter__ callback.
609 elms->set(0, Heap::true_value());
610 elms->set(1, FixedArray::cast(structure)->get(0));
611 elms->set(2, FixedArray::cast(structure)->get(1));
612 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000613 return Heap::undefined_value();
614 }
615 } else {
616 elms->set(0, Heap::false_value());
617 elms->set(1, result.GetLazyValue());
618 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
619 }
620
621 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000622 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000623 return *desc;
624}
625
626
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000627static Object* Runtime_IsExtensible(Arguments args) {
628 ASSERT(args.length() == 1);
629 CONVERT_CHECKED(JSObject, obj, args[0]);
630 return obj->map()->is_extensible() ? Heap::true_value()
631 : Heap::false_value();
632}
633
634
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000635static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000636 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000637 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000638 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
639 CONVERT_ARG_CHECKED(String, pattern, 1);
640 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000641 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
642 if (result.is_null()) return Failure::Exception();
643 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000644}
645
646
647static Object* Runtime_CreateApiFunction(Arguments args) {
648 HandleScope scope;
649 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000650 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000651 return *Factory::CreateApiFunction(data);
652}
653
654
655static Object* Runtime_IsTemplate(Arguments args) {
656 ASSERT(args.length() == 1);
657 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000658 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000659 return Heap::ToBoolean(result);
660}
661
662
663static Object* Runtime_GetTemplateField(Arguments args) {
664 ASSERT(args.length() == 2);
665 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000666 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000667 int index = field->value();
668 int offset = index * kPointerSize + HeapObject::kHeaderSize;
669 InstanceType type = templ->map()->instance_type();
670 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
671 type == OBJECT_TEMPLATE_INFO_TYPE);
672 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000673 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000674 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
675 } else {
676 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
677 }
678 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000679}
680
681
ager@chromium.org870a0b62008-11-04 11:43:05 +0000682static Object* Runtime_DisableAccessChecks(Arguments args) {
683 ASSERT(args.length() == 1);
684 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000685 Map* old_map = object->map();
686 bool needs_access_checks = old_map->is_access_check_needed();
687 if (needs_access_checks) {
688 // Copy map so it won't interfere constructor's initial map.
689 Object* new_map = old_map->CopyDropTransitions();
690 if (new_map->IsFailure()) return new_map;
691
692 Map::cast(new_map)->set_is_access_check_needed(false);
693 object->set_map(Map::cast(new_map));
694 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000695 return needs_access_checks ? Heap::true_value() : Heap::false_value();
696}
697
698
699static Object* Runtime_EnableAccessChecks(Arguments args) {
700 ASSERT(args.length() == 1);
701 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000702 Map* old_map = object->map();
703 if (!old_map->is_access_check_needed()) {
704 // Copy map so it won't interfere constructor's initial map.
705 Object* new_map = old_map->CopyDropTransitions();
706 if (new_map->IsFailure()) return new_map;
707
708 Map::cast(new_map)->set_is_access_check_needed(true);
709 object->set_map(Map::cast(new_map));
710 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000711 return Heap::undefined_value();
712}
713
714
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000715static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
716 HandleScope scope;
717 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
718 Handle<Object> args[2] = { type_handle, name };
719 Handle<Object> error =
720 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
721 return Top::Throw(*error);
722}
723
724
725static Object* Runtime_DeclareGlobals(Arguments args) {
726 HandleScope scope;
727 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
728
ager@chromium.org3811b432009-10-28 14:53:37 +0000729 Handle<Context> context = args.at<Context>(0);
730 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000731 bool is_eval = Smi::cast(args[2])->value() == 1;
732
733 // Compute the property attributes. According to ECMA-262, section
734 // 13, page 71, the property must be read-only and
735 // non-deletable. However, neither SpiderMonkey nor KJS creates the
736 // property as read-only, so we don't either.
737 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
738
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739 // Traverse the name/value pairs and set the properties.
740 int length = pairs->length();
741 for (int i = 0; i < length; i += 2) {
742 HandleScope scope;
743 Handle<String> name(String::cast(pairs->get(i)));
744 Handle<Object> value(pairs->get(i + 1));
745
746 // We have to declare a global const property. To capture we only
747 // assign to it when evaluating the assignment for "const x =
748 // <expr>" the initial value is the hole.
749 bool is_const_property = value->IsTheHole();
750
751 if (value->IsUndefined() || is_const_property) {
752 // Lookup the property in the global object, and don't set the
753 // value of the variable if the property is already there.
754 LookupResult lookup;
755 global->Lookup(*name, &lookup);
756 if (lookup.IsProperty()) {
757 // Determine if the property is local by comparing the holder
758 // against the global object. The information will be used to
759 // avoid throwing re-declaration errors when declaring
760 // variables or constants that exist in the prototype chain.
761 bool is_local = (*global == lookup.holder());
762 // Get the property attributes and determine if the property is
763 // read-only.
764 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
765 bool is_read_only = (attributes & READ_ONLY) != 0;
766 if (lookup.type() == INTERCEPTOR) {
767 // If the interceptor says the property is there, we
768 // just return undefined without overwriting the property.
769 // Otherwise, we continue to setting the property.
770 if (attributes != ABSENT) {
771 // Check if the existing property conflicts with regards to const.
772 if (is_local && (is_read_only || is_const_property)) {
773 const char* type = (is_read_only) ? "const" : "var";
774 return ThrowRedeclarationError(type, name);
775 };
776 // The property already exists without conflicting: Go to
777 // the next declaration.
778 continue;
779 }
780 // Fall-through and introduce the absent property by using
781 // SetProperty.
782 } else {
783 if (is_local && (is_read_only || is_const_property)) {
784 const char* type = (is_read_only) ? "const" : "var";
785 return ThrowRedeclarationError(type, name);
786 }
787 // The property already exists without conflicting: Go to
788 // the next declaration.
789 continue;
790 }
791 }
792 } else {
793 // Copy the function and update its context. Use it as value.
794 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
795 Handle<JSFunction> function =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +0000796 Factory::NewFunctionFromBoilerplate(boilerplate, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000797 value = function;
798 }
799
800 LookupResult lookup;
801 global->LocalLookup(*name, &lookup);
802
803 PropertyAttributes attributes = is_const_property
804 ? static_cast<PropertyAttributes>(base | READ_ONLY)
805 : base;
806
807 if (lookup.IsProperty()) {
808 // There's a local property that we need to overwrite because
809 // we're either declaring a function or there's an interceptor
810 // that claims the property is absent.
811
812 // Check for conflicting re-declarations. We cannot have
813 // conflicting types in case of intercepted properties because
814 // they are absent.
815 if (lookup.type() != INTERCEPTOR &&
816 (lookup.IsReadOnly() || is_const_property)) {
817 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
818 return ThrowRedeclarationError(type, name);
819 }
820 SetProperty(global, name, value, attributes);
821 } else {
822 // If a property with this name does not already exist on the
823 // global object add the property locally. We take special
824 // precautions to always add it as a local property even in case
825 // of callbacks in the prototype chain (this rules out using
826 // SetProperty). Also, we must use the handle-based version to
827 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000828 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000829 }
830 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000831
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000832 return Heap::undefined_value();
833}
834
835
836static Object* Runtime_DeclareContextSlot(Arguments args) {
837 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000839
ager@chromium.org7c537e22008-10-16 08:43:32 +0000840 CONVERT_ARG_CHECKED(Context, context, 0);
841 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000843 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000844 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000845 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000846
847 // Declarations are always done in the function context.
848 context = Handle<Context>(context->fcontext());
849
850 int index;
851 PropertyAttributes attributes;
852 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000853 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000854 context->Lookup(name, flags, &index, &attributes);
855
856 if (attributes != ABSENT) {
857 // The name was declared before; check for conflicting
858 // re-declarations: This is similar to the code in parser.cc in
859 // the AstBuildingParser::Declare function.
860 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
861 // Functions are not read-only.
862 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
863 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
864 return ThrowRedeclarationError(type, name);
865 }
866
867 // Initialize it if necessary.
868 if (*initial_value != NULL) {
869 if (index >= 0) {
870 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000871 // the function context or the arguments object.
872 if (holder->IsContext()) {
873 ASSERT(holder.is_identical_to(context));
874 if (((attributes & READ_ONLY) == 0) ||
875 context->get(index)->IsTheHole()) {
876 context->set(index, *initial_value);
877 }
878 } else {
879 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000880 }
881 } else {
882 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000883 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884 SetProperty(context_ext, name, initial_value, mode);
885 }
886 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000888 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000889 // The property is not in the function context. It needs to be
890 // "declared" in the function context's extension context, or in the
891 // global context.
892 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000893 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000894 // The function context's extension context exists - use it.
895 context_ext = Handle<JSObject>(context->extension());
896 } else {
897 // The function context's extension context does not exists - allocate
898 // it.
899 context_ext = Factory::NewJSObject(Top::context_extension_function());
900 // And store it in the extension slot.
901 context->set_extension(*context_ext);
902 }
903 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000904
ager@chromium.org7c537e22008-10-16 08:43:32 +0000905 // Declare the property by setting it to the initial value if provided,
906 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
907 // constant declarations).
908 ASSERT(!context_ext->HasLocalProperty(*name));
909 Handle<Object> value(Heap::undefined_value());
910 if (*initial_value != NULL) value = initial_value;
911 SetProperty(context_ext, name, value, mode);
912 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
913 }
914
915 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000916}
917
918
919static Object* Runtime_InitializeVarGlobal(Arguments args) {
920 NoHandleAllocation nha;
921
922 // Determine if we need to assign to the variable if it already
923 // exists (based on the number of arguments).
924 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
925 bool assign = args.length() == 2;
926
927 CONVERT_ARG_CHECKED(String, name, 0);
928 GlobalObject* global = Top::context()->global();
929
930 // According to ECMA-262, section 12.2, page 62, the property must
931 // not be deletable.
932 PropertyAttributes attributes = DONT_DELETE;
933
934 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000935 // there, there is a property with this name in the prototype chain.
936 // We follow Safari and Firefox behavior and only set the property
937 // locally if there is an explicit initialization value that we have
938 // to assign to the property. When adding the property we take
939 // special precautions to always add it as a local property even in
940 // case of callbacks in the prototype chain (this rules out using
941 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
942 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000943 // Note that objects can have hidden prototypes, so we need to traverse
944 // the whole chain of hidden prototypes to do a 'local' lookup.
945 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000946 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000947 while (true) {
948 real_holder->LocalLookup(*name, &lookup);
949 if (lookup.IsProperty()) {
950 // Determine if this is a redeclaration of something read-only.
951 if (lookup.IsReadOnly()) {
952 // If we found readonly property on one of hidden prototypes,
953 // just shadow it.
954 if (real_holder != Top::context()->global()) break;
955 return ThrowRedeclarationError("const", name);
956 }
957
958 // Determine if this is a redeclaration of an intercepted read-only
959 // property and figure out if the property exists at all.
960 bool found = true;
961 PropertyType type = lookup.type();
962 if (type == INTERCEPTOR) {
963 HandleScope handle_scope;
964 Handle<JSObject> holder(real_holder);
965 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
966 real_holder = *holder;
967 if (intercepted == ABSENT) {
968 // The interceptor claims the property isn't there. We need to
969 // make sure to introduce it.
970 found = false;
971 } else if ((intercepted & READ_ONLY) != 0) {
972 // The property is present, but read-only. Since we're trying to
973 // overwrite it with a variable declaration we must throw a
974 // re-declaration error. However if we found readonly property
975 // on one of hidden prototypes, just shadow it.
976 if (real_holder != Top::context()->global()) break;
977 return ThrowRedeclarationError("const", name);
978 }
979 }
980
981 if (found && !assign) {
982 // The global property is there and we're not assigning any value
983 // to it. Just return.
984 return Heap::undefined_value();
985 }
986
987 // Assign the value (or undefined) to the property.
988 Object* value = (assign) ? args[1] : Heap::undefined_value();
989 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000990 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000991
992 Object* proto = real_holder->GetPrototype();
993 if (!proto->IsJSObject())
994 break;
995
996 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
997 break;
998
999 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001000 }
1001
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001002 global = Top::context()->global();
1003 if (assign) {
1004 return global->IgnoreAttributesAndSetLocalProperty(*name,
1005 args[1],
1006 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001008 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001009}
1010
1011
1012static Object* Runtime_InitializeConstGlobal(Arguments args) {
1013 // All constants are declared with an initial value. The name
1014 // of the constant is the first argument and the initial value
1015 // is the second.
1016 RUNTIME_ASSERT(args.length() == 2);
1017 CONVERT_ARG_CHECKED(String, name, 0);
1018 Handle<Object> value = args.at<Object>(1);
1019
1020 // Get the current global object from top.
1021 GlobalObject* global = Top::context()->global();
1022
1023 // According to ECMA-262, section 12.2, page 62, the property must
1024 // not be deletable. Since it's a const, it must be READ_ONLY too.
1025 PropertyAttributes attributes =
1026 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1027
1028 // Lookup the property locally in the global object. If it isn't
1029 // there, we add the property and take special precautions to always
1030 // add it as a local property even in case of callbacks in the
1031 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001032 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001033 LookupResult lookup;
1034 global->LocalLookup(*name, &lookup);
1035 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001036 return global->IgnoreAttributesAndSetLocalProperty(*name,
1037 *value,
1038 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001039 }
1040
1041 // Determine if this is a redeclaration of something not
1042 // read-only. In case the result is hidden behind an interceptor we
1043 // need to ask it for the property attributes.
1044 if (!lookup.IsReadOnly()) {
1045 if (lookup.type() != INTERCEPTOR) {
1046 return ThrowRedeclarationError("var", name);
1047 }
1048
1049 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1050
1051 // Throw re-declaration error if the intercepted property is present
1052 // but not read-only.
1053 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1054 return ThrowRedeclarationError("var", name);
1055 }
1056
1057 // Restore global object from context (in case of GC) and continue
1058 // with setting the value because the property is either absent or
1059 // read-only. We also have to do redo the lookup.
1060 global = Top::context()->global();
1061
1062 // BUG 1213579: Handle the case where we have to set a read-only
1063 // property through an interceptor and only do it if it's
1064 // uninitialized, e.g. the hole. Nirk...
1065 global->SetProperty(*name, *value, attributes);
1066 return *value;
1067 }
1068
1069 // Set the value, but only we're assigning the initial value to a
1070 // constant. For now, we determine this by checking if the
1071 // current value is the hole.
1072 PropertyType type = lookup.type();
1073 if (type == FIELD) {
1074 FixedArray* properties = global->properties();
1075 int index = lookup.GetFieldIndex();
1076 if (properties->get(index)->IsTheHole()) {
1077 properties->set(index, *value);
1078 }
1079 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001080 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1081 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001082 }
1083 } else {
1084 // Ignore re-initialization of constants that have already been
1085 // assigned a function value.
1086 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1087 }
1088
1089 // Use the set value as the result of the operation.
1090 return *value;
1091}
1092
1093
1094static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1095 HandleScope scope;
1096 ASSERT(args.length() == 3);
1097
1098 Handle<Object> value(args[0]);
1099 ASSERT(!value->IsTheHole());
1100 CONVERT_ARG_CHECKED(Context, context, 1);
1101 Handle<String> name(String::cast(args[2]));
1102
1103 // Initializations are always done in the function context.
1104 context = Handle<Context>(context->fcontext());
1105
1106 int index;
1107 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001108 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001109 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001110 context->Lookup(name, flags, &index, &attributes);
1111
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001112 // In most situations, the property introduced by the const
1113 // declaration should be present in the context extension object.
1114 // However, because declaration and initialization are separate, the
1115 // property might have been deleted (if it was introduced by eval)
1116 // before we reach the initialization point.
1117 //
1118 // Example:
1119 //
1120 // function f() { eval("delete x; const x;"); }
1121 //
1122 // In that case, the initialization behaves like a normal assignment
1123 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001124 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001125 // Property was found in a context.
1126 if (holder->IsContext()) {
1127 // The holder cannot be the function context. If it is, there
1128 // should have been a const redeclaration error when declaring
1129 // the const property.
1130 ASSERT(!holder.is_identical_to(context));
1131 if ((attributes & READ_ONLY) == 0) {
1132 Handle<Context>::cast(holder)->set(index, *value);
1133 }
1134 } else {
1135 // The holder is an arguments object.
1136 ASSERT((attributes & READ_ONLY) == 0);
1137 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001138 }
1139 return *value;
1140 }
1141
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001142 // The property could not be found, we introduce it in the global
1143 // context.
1144 if (attributes == ABSENT) {
1145 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1146 SetProperty(global, name, value, NONE);
1147 return *value;
1148 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001149
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001150 // The property was present in a context extension object.
1151 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001152
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001153 if (*context_ext == context->extension()) {
1154 // This is the property that was introduced by the const
1155 // declaration. Set it if it hasn't been set before. NOTE: We
1156 // cannot use GetProperty() to get the current value as it
1157 // 'unholes' the value.
1158 LookupResult lookup;
1159 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1160 ASSERT(lookup.IsProperty()); // the property was declared
1161 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1162
1163 PropertyType type = lookup.type();
1164 if (type == FIELD) {
1165 FixedArray* properties = context_ext->properties();
1166 int index = lookup.GetFieldIndex();
1167 if (properties->get(index)->IsTheHole()) {
1168 properties->set(index, *value);
1169 }
1170 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001171 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1172 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001173 }
1174 } else {
1175 // We should not reach here. Any real, named property should be
1176 // either a field or a dictionary slot.
1177 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001178 }
1179 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001180 // The property was found in a different context extension object.
1181 // Set it if it is not a read-only property.
1182 if ((attributes & READ_ONLY) == 0) {
1183 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1184 // Setting a property might throw an exception. Exceptions
1185 // are converted to empty handles in handle operations. We
1186 // need to convert back to exceptions here.
1187 if (set.is_null()) {
1188 ASSERT(Top::has_pending_exception());
1189 return Failure::Exception();
1190 }
1191 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001193
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001194 return *value;
1195}
1196
1197
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001198static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1199 Arguments args) {
1200 HandleScope scope;
1201 ASSERT(args.length() == 2);
1202 CONVERT_ARG_CHECKED(JSObject, object, 0);
1203 CONVERT_SMI_CHECKED(properties, args[1]);
1204 if (object->HasFastProperties()) {
1205 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1206 }
1207 return *object;
1208}
1209
1210
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001211static Object* Runtime_RegExpExec(Arguments args) {
1212 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001213 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001214 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1215 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001216 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001217 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001218 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001219 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001220 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001221 RUNTIME_ASSERT(index >= 0);
1222 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001223 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001224 Handle<Object> result = RegExpImpl::Exec(regexp,
1225 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001226 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001227 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001228 if (result.is_null()) return Failure::Exception();
1229 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001230}
1231
1232
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001233static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1234 HandleScope scope;
1235 ASSERT(args.length() == 4);
1236 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1237 int index = Smi::cast(args[1])->value();
1238 Handle<String> pattern = args.at<String>(2);
1239 Handle<String> flags = args.at<String>(3);
1240
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001241 // Get the RegExp function from the context in the literals array.
1242 // This is the RegExp function from the context in which the
1243 // function was created. We do not use the RegExp function from the
1244 // current global context because this might be the RegExp function
1245 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001246 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001247 Handle<JSFunction>(
1248 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 // Compute the regular expression literal.
1250 bool has_pending_exception;
1251 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001252 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1253 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001254 if (has_pending_exception) {
1255 ASSERT(Top::has_pending_exception());
1256 return Failure::Exception();
1257 }
1258 literals->set(index, *regexp);
1259 return *regexp;
1260}
1261
1262
1263static Object* Runtime_FunctionGetName(Arguments args) {
1264 NoHandleAllocation ha;
1265 ASSERT(args.length() == 1);
1266
1267 CONVERT_CHECKED(JSFunction, f, args[0]);
1268 return f->shared()->name();
1269}
1270
1271
ager@chromium.org236ad962008-09-25 09:45:57 +00001272static Object* Runtime_FunctionSetName(Arguments args) {
1273 NoHandleAllocation ha;
1274 ASSERT(args.length() == 2);
1275
1276 CONVERT_CHECKED(JSFunction, f, args[0]);
1277 CONVERT_CHECKED(String, name, args[1]);
1278 f->shared()->set_name(name);
1279 return Heap::undefined_value();
1280}
1281
1282
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001283static Object* Runtime_FunctionGetScript(Arguments args) {
1284 HandleScope scope;
1285 ASSERT(args.length() == 1);
1286
1287 CONVERT_CHECKED(JSFunction, fun, args[0]);
1288 Handle<Object> script = Handle<Object>(fun->shared()->script());
1289 if (!script->IsScript()) return Heap::undefined_value();
1290
1291 return *GetScriptWrapper(Handle<Script>::cast(script));
1292}
1293
1294
1295static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1296 NoHandleAllocation ha;
1297 ASSERT(args.length() == 1);
1298
1299 CONVERT_CHECKED(JSFunction, f, args[0]);
1300 return f->shared()->GetSourceCode();
1301}
1302
1303
1304static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1305 NoHandleAllocation ha;
1306 ASSERT(args.length() == 1);
1307
1308 CONVERT_CHECKED(JSFunction, fun, args[0]);
1309 int pos = fun->shared()->start_position();
1310 return Smi::FromInt(pos);
1311}
1312
1313
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001314static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1315 ASSERT(args.length() == 2);
1316
1317 CONVERT_CHECKED(JSFunction, fun, args[0]);
1318 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1319
1320 Code* code = fun->code();
1321 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1322
1323 Address pc = code->address() + offset;
1324 return Smi::FromInt(fun->code()->SourcePosition(pc));
1325}
1326
1327
1328
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001329static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1330 NoHandleAllocation ha;
1331 ASSERT(args.length() == 2);
1332
1333 CONVERT_CHECKED(JSFunction, fun, args[0]);
1334 CONVERT_CHECKED(String, name, args[1]);
1335 fun->SetInstanceClassName(name);
1336 return Heap::undefined_value();
1337}
1338
1339
1340static Object* Runtime_FunctionSetLength(Arguments args) {
1341 NoHandleAllocation ha;
1342 ASSERT(args.length() == 2);
1343
1344 CONVERT_CHECKED(JSFunction, fun, args[0]);
1345 CONVERT_CHECKED(Smi, length, args[1]);
1346 fun->shared()->set_length(length->value());
1347 return length;
1348}
1349
1350
1351static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001352 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001353 ASSERT(args.length() == 2);
1354
1355 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001356 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1357 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001358 return args[0]; // return TOS
1359}
1360
1361
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001362static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1363 NoHandleAllocation ha;
1364 ASSERT(args.length() == 1);
1365
1366 CONVERT_CHECKED(JSFunction, f, args[0]);
1367 // The function_data field of the shared function info is used exclusively by
1368 // the API.
1369 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1370 : Heap::false_value();
1371}
1372
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001373static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1374 NoHandleAllocation ha;
1375 ASSERT(args.length() == 1);
1376
1377 CONVERT_CHECKED(JSFunction, f, args[0]);
1378 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1379}
1380
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001381
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001382static Object* Runtime_SetCode(Arguments args) {
1383 HandleScope scope;
1384 ASSERT(args.length() == 2);
1385
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001386 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001387 Handle<Object> code = args.at<Object>(1);
1388
1389 Handle<Context> context(target->context());
1390
1391 if (!code->IsNull()) {
1392 RUNTIME_ASSERT(code->IsJSFunction());
1393 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001394 Handle<SharedFunctionInfo> shared(fun->shared());
1395 SetExpectedNofProperties(target, shared->expected_nof_properties());
1396
1397 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001398 return Failure::Exception();
1399 }
1400 // Set the code, formal parameter count, and the length of the target
1401 // function.
1402 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001403 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001404 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001405 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001406 // Set the source code of the target function to undefined.
1407 // SetCode is only used for built-in constructors like String,
1408 // Array, and Object, and some web code
1409 // doesn't like seeing source code for constructors.
1410 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001411 // Clear the optimization hints related to the compiled code as these are no
1412 // longer valid when the code is overwritten.
1413 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001414 context = Handle<Context>(fun->context());
1415
1416 // Make sure we get a fresh copy of the literal vector to avoid
1417 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001418 int number_of_literals = fun->NumberOfLiterals();
1419 Handle<FixedArray> literals =
1420 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001421 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001422 // Insert the object, regexp and array functions in the literals
1423 // array prefix. These are the functions that will be used when
1424 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001425 literals->set(JSFunction::kLiteralGlobalContextIndex,
1426 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001427 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001428 // It's okay to skip the write barrier here because the literals
1429 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001430 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001431 }
1432
1433 target->set_context(*context);
1434 return *target;
1435}
1436
1437
1438static Object* CharCodeAt(String* subject, Object* index) {
1439 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001440 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001441 // Flatten the string. If someone wants to get a char at an index
1442 // in a cons string, it is likely that more indices will be
1443 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001444 Object* flat = subject->TryFlatten();
1445 if (flat->IsFailure()) return flat;
1446 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001447 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001448 return Heap::nan_value();
1449 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001450 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001451}
1452
1453
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001454static Object* CharFromCode(Object* char_code) {
1455 uint32_t code;
1456 if (Array::IndexFromObject(char_code, &code)) {
1457 if (code <= 0xffff) {
1458 return Heap::LookupSingleCharacterStringFromCode(code);
1459 }
1460 }
1461 return Heap::empty_string();
1462}
1463
1464
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001465static Object* Runtime_StringCharCodeAt(Arguments args) {
1466 NoHandleAllocation ha;
1467 ASSERT(args.length() == 2);
1468
1469 CONVERT_CHECKED(String, subject, args[0]);
1470 Object* index = args[1];
1471 return CharCodeAt(subject, index);
1472}
1473
1474
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001475static Object* Runtime_StringCharAt(Arguments args) {
1476 NoHandleAllocation ha;
1477 ASSERT(args.length() == 2);
1478
1479 CONVERT_CHECKED(String, subject, args[0]);
1480 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001481 Object* code = CharCodeAt(subject, index);
1482 if (code == Heap::nan_value()) {
1483 return Heap::undefined_value();
1484 }
1485 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001486}
1487
1488
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001489static Object* Runtime_CharFromCode(Arguments args) {
1490 NoHandleAllocation ha;
1491 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001492 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001493}
1494
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001495// Forward declarations.
1496static const int kStringBuilderConcatHelperLengthBits = 11;
1497static const int kStringBuilderConcatHelperPositionBits = 19;
1498
1499template <typename schar>
1500static inline void StringBuilderConcatHelper(String*,
1501 schar*,
1502 FixedArray*,
1503 int);
1504
1505typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1506typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1507
1508class ReplacementStringBuilder {
1509 public:
1510 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1511 : subject_(subject),
1512 parts_(Factory::NewFixedArray(estimated_part_count)),
1513 part_count_(0),
1514 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001515 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001516 // Require a non-zero initial size. Ensures that doubling the size to
1517 // extend the array will work.
1518 ASSERT(estimated_part_count > 0);
1519 }
1520
1521 void EnsureCapacity(int elements) {
1522 int length = parts_->length();
1523 int required_length = part_count_ + elements;
1524 if (length < required_length) {
1525 int new_length = length;
1526 do {
1527 new_length *= 2;
1528 } while (new_length < required_length);
1529 Handle<FixedArray> extended_array =
1530 Factory::NewFixedArray(new_length);
1531 parts_->CopyTo(0, *extended_array, 0, part_count_);
1532 parts_ = extended_array;
1533 }
1534 }
1535
1536 void AddSubjectSlice(int from, int to) {
1537 ASSERT(from >= 0);
1538 int length = to - from;
1539 ASSERT(length > 0);
1540 // Can we encode the slice in 11 bits for length and 19 bits for
1541 // start position - as used by StringBuilderConcatHelper?
1542 if (StringBuilderSubstringLength::is_valid(length) &&
1543 StringBuilderSubstringPosition::is_valid(from)) {
1544 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1545 StringBuilderSubstringPosition::encode(from);
1546 AddElement(Smi::FromInt(encoded_slice));
1547 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001548 // Otherwise encode as two smis.
1549 AddElement(Smi::FromInt(-length));
1550 AddElement(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001551 }
1552 IncrementCharacterCount(length);
1553 }
1554
1555
1556 void AddString(Handle<String> string) {
1557 int length = string->length();
1558 ASSERT(length > 0);
1559 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001560 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001561 is_ascii_ = false;
1562 }
1563 IncrementCharacterCount(length);
1564 }
1565
1566
1567 Handle<String> ToString() {
1568 if (part_count_ == 0) {
1569 return Factory::empty_string();
1570 }
1571
1572 Handle<String> joined_string;
1573 if (is_ascii_) {
1574 joined_string = NewRawAsciiString(character_count_);
1575 AssertNoAllocation no_alloc;
1576 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1577 char* char_buffer = seq->GetChars();
1578 StringBuilderConcatHelper(*subject_,
1579 char_buffer,
1580 *parts_,
1581 part_count_);
1582 } else {
1583 // Non-ASCII.
1584 joined_string = NewRawTwoByteString(character_count_);
1585 AssertNoAllocation no_alloc;
1586 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1587 uc16* char_buffer = seq->GetChars();
1588 StringBuilderConcatHelper(*subject_,
1589 char_buffer,
1590 *parts_,
1591 part_count_);
1592 }
1593 return joined_string;
1594 }
1595
1596
1597 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001598 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001599 V8::FatalProcessOutOfMemory("String.replace result too large.");
1600 }
1601 character_count_ += by;
1602 }
1603
1604 private:
1605
1606 Handle<String> NewRawAsciiString(int size) {
1607 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1608 }
1609
1610
1611 Handle<String> NewRawTwoByteString(int size) {
1612 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1613 }
1614
1615
1616 void AddElement(Object* element) {
1617 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001618 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001619 parts_->set(part_count_, element);
1620 part_count_++;
1621 }
1622
1623 Handle<String> subject_;
1624 Handle<FixedArray> parts_;
1625 int part_count_;
1626 int character_count_;
1627 bool is_ascii_;
1628};
1629
1630
1631class CompiledReplacement {
1632 public:
1633 CompiledReplacement()
1634 : parts_(1), replacement_substrings_(0) {}
1635
1636 void Compile(Handle<String> replacement,
1637 int capture_count,
1638 int subject_length);
1639
1640 void Apply(ReplacementStringBuilder* builder,
1641 int match_from,
1642 int match_to,
1643 Handle<JSArray> last_match_info);
1644
1645 // Number of distinct parts of the replacement pattern.
1646 int parts() {
1647 return parts_.length();
1648 }
1649 private:
1650 enum PartType {
1651 SUBJECT_PREFIX = 1,
1652 SUBJECT_SUFFIX,
1653 SUBJECT_CAPTURE,
1654 REPLACEMENT_SUBSTRING,
1655 REPLACEMENT_STRING,
1656
1657 NUMBER_OF_PART_TYPES
1658 };
1659
1660 struct ReplacementPart {
1661 static inline ReplacementPart SubjectMatch() {
1662 return ReplacementPart(SUBJECT_CAPTURE, 0);
1663 }
1664 static inline ReplacementPart SubjectCapture(int capture_index) {
1665 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1666 }
1667 static inline ReplacementPart SubjectPrefix() {
1668 return ReplacementPart(SUBJECT_PREFIX, 0);
1669 }
1670 static inline ReplacementPart SubjectSuffix(int subject_length) {
1671 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1672 }
1673 static inline ReplacementPart ReplacementString() {
1674 return ReplacementPart(REPLACEMENT_STRING, 0);
1675 }
1676 static inline ReplacementPart ReplacementSubString(int from, int to) {
1677 ASSERT(from >= 0);
1678 ASSERT(to > from);
1679 return ReplacementPart(-from, to);
1680 }
1681
1682 // If tag <= 0 then it is the negation of a start index of a substring of
1683 // the replacement pattern, otherwise it's a value from PartType.
1684 ReplacementPart(int tag, int data)
1685 : tag(tag), data(data) {
1686 // Must be non-positive or a PartType value.
1687 ASSERT(tag < NUMBER_OF_PART_TYPES);
1688 }
1689 // Either a value of PartType or a non-positive number that is
1690 // the negation of an index into the replacement string.
1691 int tag;
1692 // The data value's interpretation depends on the value of tag:
1693 // tag == SUBJECT_PREFIX ||
1694 // tag == SUBJECT_SUFFIX: data is unused.
1695 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1696 // tag == REPLACEMENT_SUBSTRING ||
1697 // tag == REPLACEMENT_STRING: data is index into array of substrings
1698 // of the replacement string.
1699 // tag <= 0: Temporary representation of the substring of the replacement
1700 // string ranging over -tag .. data.
1701 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1702 // substring objects.
1703 int data;
1704 };
1705
1706 template<typename Char>
1707 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1708 Vector<Char> characters,
1709 int capture_count,
1710 int subject_length) {
1711 int length = characters.length();
1712 int last = 0;
1713 for (int i = 0; i < length; i++) {
1714 Char c = characters[i];
1715 if (c == '$') {
1716 int next_index = i + 1;
1717 if (next_index == length) { // No next character!
1718 break;
1719 }
1720 Char c2 = characters[next_index];
1721 switch (c2) {
1722 case '$':
1723 if (i > last) {
1724 // There is a substring before. Include the first "$".
1725 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1726 last = next_index + 1; // Continue after the second "$".
1727 } else {
1728 // Let the next substring start with the second "$".
1729 last = next_index;
1730 }
1731 i = next_index;
1732 break;
1733 case '`':
1734 if (i > last) {
1735 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1736 }
1737 parts->Add(ReplacementPart::SubjectPrefix());
1738 i = next_index;
1739 last = i + 1;
1740 break;
1741 case '\'':
1742 if (i > last) {
1743 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1744 }
1745 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1746 i = next_index;
1747 last = i + 1;
1748 break;
1749 case '&':
1750 if (i > last) {
1751 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1752 }
1753 parts->Add(ReplacementPart::SubjectMatch());
1754 i = next_index;
1755 last = i + 1;
1756 break;
1757 case '0':
1758 case '1':
1759 case '2':
1760 case '3':
1761 case '4':
1762 case '5':
1763 case '6':
1764 case '7':
1765 case '8':
1766 case '9': {
1767 int capture_ref = c2 - '0';
1768 if (capture_ref > capture_count) {
1769 i = next_index;
1770 continue;
1771 }
1772 int second_digit_index = next_index + 1;
1773 if (second_digit_index < length) {
1774 // Peek ahead to see if we have two digits.
1775 Char c3 = characters[second_digit_index];
1776 if ('0' <= c3 && c3 <= '9') { // Double digits.
1777 int double_digit_ref = capture_ref * 10 + c3 - '0';
1778 if (double_digit_ref <= capture_count) {
1779 next_index = second_digit_index;
1780 capture_ref = double_digit_ref;
1781 }
1782 }
1783 }
1784 if (capture_ref > 0) {
1785 if (i > last) {
1786 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1787 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001788 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001789 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1790 last = next_index + 1;
1791 }
1792 i = next_index;
1793 break;
1794 }
1795 default:
1796 i = next_index;
1797 break;
1798 }
1799 }
1800 }
1801 if (length > last) {
1802 if (last == 0) {
1803 parts->Add(ReplacementPart::ReplacementString());
1804 } else {
1805 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1806 }
1807 }
1808 }
1809
1810 ZoneList<ReplacementPart> parts_;
1811 ZoneList<Handle<String> > replacement_substrings_;
1812};
1813
1814
1815void CompiledReplacement::Compile(Handle<String> replacement,
1816 int capture_count,
1817 int subject_length) {
1818 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001819 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001820 AssertNoAllocation no_alloc;
1821 ParseReplacementPattern(&parts_,
1822 replacement->ToAsciiVector(),
1823 capture_count,
1824 subject_length);
1825 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001826 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001827 AssertNoAllocation no_alloc;
1828
1829 ParseReplacementPattern(&parts_,
1830 replacement->ToUC16Vector(),
1831 capture_count,
1832 subject_length);
1833 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001834 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001835 int substring_index = 0;
1836 for (int i = 0, n = parts_.length(); i < n; i++) {
1837 int tag = parts_[i].tag;
1838 if (tag <= 0) { // A replacement string slice.
1839 int from = -tag;
1840 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001841 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001842 parts_[i].tag = REPLACEMENT_SUBSTRING;
1843 parts_[i].data = substring_index;
1844 substring_index++;
1845 } else if (tag == REPLACEMENT_STRING) {
1846 replacement_substrings_.Add(replacement);
1847 parts_[i].data = substring_index;
1848 substring_index++;
1849 }
1850 }
1851}
1852
1853
1854void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1855 int match_from,
1856 int match_to,
1857 Handle<JSArray> last_match_info) {
1858 for (int i = 0, n = parts_.length(); i < n; i++) {
1859 ReplacementPart part = parts_[i];
1860 switch (part.tag) {
1861 case SUBJECT_PREFIX:
1862 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1863 break;
1864 case SUBJECT_SUFFIX: {
1865 int subject_length = part.data;
1866 if (match_to < subject_length) {
1867 builder->AddSubjectSlice(match_to, subject_length);
1868 }
1869 break;
1870 }
1871 case SUBJECT_CAPTURE: {
1872 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001873 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001874 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1875 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1876 if (from >= 0 && to > from) {
1877 builder->AddSubjectSlice(from, to);
1878 }
1879 break;
1880 }
1881 case REPLACEMENT_SUBSTRING:
1882 case REPLACEMENT_STRING:
1883 builder->AddString(replacement_substrings_[part.data]);
1884 break;
1885 default:
1886 UNREACHABLE();
1887 }
1888 }
1889}
1890
1891
1892
1893static Object* StringReplaceRegExpWithString(String* subject,
1894 JSRegExp* regexp,
1895 String* replacement,
1896 JSArray* last_match_info) {
1897 ASSERT(subject->IsFlat());
1898 ASSERT(replacement->IsFlat());
1899
1900 HandleScope handles;
1901
1902 int length = subject->length();
1903 Handle<String> subject_handle(subject);
1904 Handle<JSRegExp> regexp_handle(regexp);
1905 Handle<String> replacement_handle(replacement);
1906 Handle<JSArray> last_match_info_handle(last_match_info);
1907 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1908 subject_handle,
1909 0,
1910 last_match_info_handle);
1911 if (match.is_null()) {
1912 return Failure::Exception();
1913 }
1914 if (match->IsNull()) {
1915 return *subject_handle;
1916 }
1917
1918 int capture_count = regexp_handle->CaptureCount();
1919
1920 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001921 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001922 CompiledReplacement compiled_replacement;
1923 compiled_replacement.Compile(replacement_handle,
1924 capture_count,
1925 length);
1926
1927 bool is_global = regexp_handle->GetFlags().is_global();
1928
1929 // Guessing the number of parts that the final result string is built
1930 // from. Global regexps can match any number of times, so we guess
1931 // conservatively.
1932 int expected_parts =
1933 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1934 ReplacementStringBuilder builder(subject_handle, expected_parts);
1935
1936 // Index of end of last match.
1937 int prev = 0;
1938
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001939 // Number of parts added by compiled replacement plus preceeding
1940 // string and possibly suffix after last match. It is possible for
1941 // all components to use two elements when encoded as two smis.
1942 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001943 bool matched = true;
1944 do {
1945 ASSERT(last_match_info_handle->HasFastElements());
1946 // Increase the capacity of the builder before entering local handle-scope,
1947 // so its internal buffer can safely allocate a new handle if it grows.
1948 builder.EnsureCapacity(parts_added_per_loop);
1949
1950 HandleScope loop_scope;
1951 int start, end;
1952 {
1953 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001954 FixedArray* match_info_array =
1955 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001956
1957 ASSERT_EQ(capture_count * 2 + 2,
1958 RegExpImpl::GetLastCaptureCount(match_info_array));
1959 start = RegExpImpl::GetCapture(match_info_array, 0);
1960 end = RegExpImpl::GetCapture(match_info_array, 1);
1961 }
1962
1963 if (prev < start) {
1964 builder.AddSubjectSlice(prev, start);
1965 }
1966 compiled_replacement.Apply(&builder,
1967 start,
1968 end,
1969 last_match_info_handle);
1970 prev = end;
1971
1972 // Only continue checking for global regexps.
1973 if (!is_global) break;
1974
1975 // Continue from where the match ended, unless it was an empty match.
1976 int next = end;
1977 if (start == end) {
1978 next = end + 1;
1979 if (next > length) break;
1980 }
1981
1982 match = RegExpImpl::Exec(regexp_handle,
1983 subject_handle,
1984 next,
1985 last_match_info_handle);
1986 if (match.is_null()) {
1987 return Failure::Exception();
1988 }
1989 matched = !match->IsNull();
1990 } while (matched);
1991
1992 if (prev < length) {
1993 builder.AddSubjectSlice(prev, length);
1994 }
1995
1996 return *(builder.ToString());
1997}
1998
1999
2000static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2001 ASSERT(args.length() == 4);
2002
2003 CONVERT_CHECKED(String, subject, args[0]);
2004 if (!subject->IsFlat()) {
2005 Object* flat_subject = subject->TryFlatten();
2006 if (flat_subject->IsFailure()) {
2007 return flat_subject;
2008 }
2009 subject = String::cast(flat_subject);
2010 }
2011
2012 CONVERT_CHECKED(String, replacement, args[2]);
2013 if (!replacement->IsFlat()) {
2014 Object* flat_replacement = replacement->TryFlatten();
2015 if (flat_replacement->IsFailure()) {
2016 return flat_replacement;
2017 }
2018 replacement = String::cast(flat_replacement);
2019 }
2020
2021 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2022 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2023
2024 ASSERT(last_match_info->HasFastElements());
2025
2026 return StringReplaceRegExpWithString(subject,
2027 regexp,
2028 replacement,
2029 last_match_info);
2030}
2031
2032
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002033
ager@chromium.org7c537e22008-10-16 08:43:32 +00002034// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2035// limit, we can fix the size of tables.
2036static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002037// Reduce alphabet to this size.
2038static const int kBMAlphabetSize = 0x100;
2039// For patterns below this length, the skip length of Boyer-Moore is too short
2040// to compensate for the algorithmic overhead compared to simple brute force.
2041static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002042
ager@chromium.org7c537e22008-10-16 08:43:32 +00002043// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2044// shift. Only allows the last kBMMaxShift characters of the needle
2045// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002046class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002047 public:
2048 BMGoodSuffixBuffers() {}
2049 inline void init(int needle_length) {
2050 ASSERT(needle_length > 1);
2051 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2052 int len = needle_length - start;
2053 biased_suffixes_ = suffixes_ - start;
2054 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2055 for (int i = 0; i <= len; i++) {
2056 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002057 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002058 }
2059 inline int& suffix(int index) {
2060 ASSERT(biased_suffixes_ + index >= suffixes_);
2061 return biased_suffixes_[index];
2062 }
2063 inline int& shift(int index) {
2064 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2065 return biased_good_suffix_shift_[index];
2066 }
2067 private:
2068 int suffixes_[kBMMaxShift + 1];
2069 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002070 int* biased_suffixes_;
2071 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002072 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2073};
2074
2075// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002076static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002077static BMGoodSuffixBuffers bmgs_buffers;
2078
2079// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002080template <typename pchar>
2081static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
2082 int start) {
2083 // Run forwards to populate bad_char_table, so that *last* instance
2084 // of character equivalence class is the one registered.
2085 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002086 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2087 : kBMAlphabetSize;
2088 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002089 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002090 } else {
2091 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002092 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002093 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002094 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002095 for (int i = start; i < pattern.length() - 1; i++) {
2096 pchar c = pattern[i];
2097 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002098 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002099 }
2100}
2101
2102template <typename pchar>
2103static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002104 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002105 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002106 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002107 // Compute Good Suffix tables.
2108 bmgs_buffers.init(m);
2109
2110 bmgs_buffers.shift(m-1) = 1;
2111 bmgs_buffers.suffix(m) = m + 1;
2112 pchar last_char = pattern[m - 1];
2113 int suffix = m + 1;
2114 for (int i = m; i > start;) {
2115 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2116 if (bmgs_buffers.shift(suffix) == len) {
2117 bmgs_buffers.shift(suffix) = suffix - i;
2118 }
2119 suffix = bmgs_buffers.suffix(suffix);
2120 }
2121 i--;
2122 suffix--;
2123 bmgs_buffers.suffix(i) = suffix;
2124 if (suffix == m) {
2125 // No suffix to extend, so we check against last_char only.
2126 while (i > start && pattern[i - 1] != last_char) {
2127 if (bmgs_buffers.shift(m) == len) {
2128 bmgs_buffers.shift(m) = m - i;
2129 }
2130 i--;
2131 bmgs_buffers.suffix(i) = m;
2132 }
2133 if (i > start) {
2134 i--;
2135 suffix--;
2136 bmgs_buffers.suffix(i) = suffix;
2137 }
2138 }
2139 }
2140 if (suffix < m) {
2141 for (int i = start; i <= m; i++) {
2142 if (bmgs_buffers.shift(i) == len) {
2143 bmgs_buffers.shift(i) = suffix - start;
2144 }
2145 if (i == suffix) {
2146 suffix = bmgs_buffers.suffix(suffix);
2147 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002148 }
2149 }
2150}
2151
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002152template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002153static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002154 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002155 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002156 }
2157 if (sizeof(pchar) == 1) {
2158 if (char_code > String::kMaxAsciiCharCode) {
2159 return -1;
2160 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002161 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002162 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002163 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002164}
2165
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002166// Restricted simplified Boyer-Moore string matching.
2167// Uses only the bad-shift table of Boyer-Moore and only uses it
2168// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002169template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002170static int BoyerMooreHorspool(Vector<const schar> subject,
2171 Vector<const pchar> pattern,
2172 int start_index,
2173 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002174 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002175 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002176 // Only preprocess at most kBMMaxShift last characters of pattern.
2177 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002178
ager@chromium.org7c537e22008-10-16 08:43:32 +00002179 BoyerMoorePopulateBadCharTable(pattern, start);
2180
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002181 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002182 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002183 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002184 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002185 // Perform search
2186 for (idx = start_index; idx <= n - m;) {
2187 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002188 int c;
2189 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002190 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002191 int shift = j - bc_occ;
2192 idx += shift;
2193 badness += 1 - shift; // at most zero, so badness cannot increase.
2194 if (idx > n - m) {
2195 *complete = true;
2196 return -1;
2197 }
2198 }
2199 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002200 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002201 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002202 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002203 return idx;
2204 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002205 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002206 // Badness increases by the number of characters we have
2207 // checked, and decreases by the number of characters we
2208 // can skip by shifting. It's a measure of how we are doing
2209 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002210 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002211 if (badness > 0) {
2212 *complete = false;
2213 return idx;
2214 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002215 }
2216 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002217 *complete = true;
2218 return -1;
2219}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002220
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002221
2222template <typename schar, typename pchar>
2223static int BoyerMooreIndexOf(Vector<const schar> subject,
2224 Vector<const pchar> pattern,
2225 int idx) {
2226 int n = subject.length();
2227 int m = pattern.length();
2228 // Only preprocess at most kBMMaxShift last characters of pattern.
2229 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2230
2231 // Build the Good Suffix table and continue searching.
2232 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2233 pchar last_char = pattern[m - 1];
2234 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002235 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002236 int j = m - 1;
2237 schar c;
2238 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002239 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002240 idx += shift;
2241 if (idx > n - m) {
2242 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002243 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002244 }
2245 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2246 if (j < 0) {
2247 return idx;
2248 } else if (j < start) {
2249 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002250 // Fall back on BMH shift.
2251 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002252 } else {
2253 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002254 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002255 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002256 if (gs_shift > shift) {
2257 shift = gs_shift;
2258 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002259 idx += shift;
2260 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002261 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002262
2263 return -1;
2264}
2265
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002266
2267template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002268static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002269 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002270 int start_index) {
2271 for (int i = start_index, n = string.length(); i < n; i++) {
2272 if (pattern_char == string[i]) {
2273 return i;
2274 }
2275 }
2276 return -1;
2277}
2278
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002279
2280template <typename schar>
2281static int SingleCharLastIndexOf(Vector<const schar> string,
2282 schar pattern_char,
2283 int start_index) {
2284 for (int i = start_index; i >= 0; i--) {
2285 if (pattern_char == string[i]) {
2286 return i;
2287 }
2288 }
2289 return -1;
2290}
2291
2292
ager@chromium.org7c537e22008-10-16 08:43:32 +00002293// Trivial string search for shorter strings.
2294// On return, if "complete" is set to true, the return value is the
2295// final result of searching for the patter in the subject.
2296// If "complete" is set to false, the return value is the index where
2297// further checking should start, i.e., it's guaranteed that the pattern
2298// does not occur at a position prior to the returned index.
2299template <typename pchar, typename schar>
2300static int SimpleIndexOf(Vector<const schar> subject,
2301 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002302 int idx,
2303 bool* complete) {
2304 // Badness is a count of how much work we have done. When we have
2305 // done enough work we decide it's probably worth switching to a better
2306 // algorithm.
2307 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002308 // We know our pattern is at least 2 characters, we cache the first so
2309 // the common case of the first character not matching is faster.
2310 pchar pattern_first_char = pattern[0];
2311
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002312 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2313 badness++;
2314 if (badness > 0) {
2315 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002316 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002317 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002318 if (subject[i] != pattern_first_char) continue;
2319 int j = 1;
2320 do {
2321 if (pattern[j] != subject[i+j]) {
2322 break;
2323 }
2324 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002325 } while (j < pattern.length());
2326 if (j == pattern.length()) {
2327 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002328 return i;
2329 }
2330 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002331 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002332 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002333 return -1;
2334}
2335
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002336// Simple indexOf that never bails out. For short patterns only.
2337template <typename pchar, typename schar>
2338static int SimpleIndexOf(Vector<const schar> subject,
2339 Vector<const pchar> pattern,
2340 int idx) {
2341 pchar pattern_first_char = pattern[0];
2342 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2343 if (subject[i] != pattern_first_char) continue;
2344 int j = 1;
2345 do {
2346 if (pattern[j] != subject[i+j]) {
2347 break;
2348 }
2349 j++;
2350 } while (j < pattern.length());
2351 if (j == pattern.length()) {
2352 return i;
2353 }
2354 }
2355 return -1;
2356}
2357
2358
2359// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002360template <typename schar, typename pchar>
2361static int StringMatchStrategy(Vector<const schar> sub,
2362 Vector<const pchar> pat,
2363 int start_index) {
2364 ASSERT(pat.length() > 1);
2365
2366 // We have an ASCII haystack and a non-ASCII needle. Check if there
2367 // really is a non-ASCII character in the needle and bail out if there
2368 // is.
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002369 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002370 for (int i = 0; i < pat.length(); i++) {
2371 uc16 c = pat[i];
2372 if (c > String::kMaxAsciiCharCode) {
2373 return -1;
2374 }
2375 }
2376 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002377 if (pat.length() < kBMMinPatternLength) {
2378 // We don't believe fancy searching can ever be more efficient.
2379 // The max shift of Boyer-Moore on a pattern of this length does
2380 // not compensate for the overhead.
2381 return SimpleIndexOf(sub, pat, start_index);
2382 }
2383 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002384 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002385 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2386 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002387 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002388 if (complete) return idx;
2389 return BoyerMooreIndexOf(sub, pat, idx);
2390}
2391
2392// Perform string match of pattern on subject, starting at start index.
2393// Caller must ensure that 0 <= start_index <= sub->length(),
2394// and should check that pat->length() + start_index <= sub->length()
2395int Runtime::StringMatch(Handle<String> sub,
2396 Handle<String> pat,
2397 int start_index) {
2398 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002399 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002400
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002401 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002402 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002403
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002404 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002405 if (start_index + pattern_length > subject_length) return -1;
2406
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002407 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002408 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002409 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002411 // character patterns linear search is necessary, so any smart
2412 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002413 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002414 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002415 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002416 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002417 if (pchar > String::kMaxAsciiCharCode) {
2418 return -1;
2419 }
2420 Vector<const char> ascii_vector =
2421 sub->ToAsciiVector().SubVector(start_index, subject_length);
2422 const void* pos = memchr(ascii_vector.start(),
2423 static_cast<const char>(pchar),
2424 static_cast<size_t>(ascii_vector.length()));
2425 if (pos == NULL) {
2426 return -1;
2427 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002428 return static_cast<int>(reinterpret_cast<const char*>(pos)
2429 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002430 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002431 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002432 }
2433
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002434 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002435 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002436 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002437
ager@chromium.org7c537e22008-10-16 08:43:32 +00002438 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2439 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002440 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002441 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002442 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002443 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002444 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002445 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002446 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002447 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002448 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002449 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002450 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002451 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002452}
2453
2454
2455static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002456 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002457 ASSERT(args.length() == 3);
2458
ager@chromium.org7c537e22008-10-16 08:43:32 +00002459 CONVERT_ARG_CHECKED(String, sub, 0);
2460 CONVERT_ARG_CHECKED(String, pat, 1);
2461
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002462 Object* index = args[2];
2463 uint32_t start_index;
2464 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2465
ager@chromium.org870a0b62008-11-04 11:43:05 +00002466 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002467 int position = Runtime::StringMatch(sub, pat, start_index);
2468 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002469}
2470
2471
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002472template <typename schar, typename pchar>
2473static int StringMatchBackwards(Vector<const schar> sub,
2474 Vector<const pchar> pat,
2475 int idx) {
2476 ASSERT(pat.length() >= 1);
2477 ASSERT(idx + pat.length() <= sub.length());
2478
2479 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2480 for (int i = 0; i < pat.length(); i++) {
2481 uc16 c = pat[i];
2482 if (c > String::kMaxAsciiCharCode) {
2483 return -1;
2484 }
2485 }
2486 }
2487
2488 pchar pattern_first_char = pat[0];
2489 for (int i = idx; i >= 0; i--) {
2490 if (sub[i] != pattern_first_char) continue;
2491 int j = 1;
2492 while (j < pat.length()) {
2493 if (pat[j] != sub[i+j]) {
2494 break;
2495 }
2496 j++;
2497 }
2498 if (j == pat.length()) {
2499 return i;
2500 }
2501 }
2502 return -1;
2503}
2504
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002505static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002506 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002507 ASSERT(args.length() == 3);
2508
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002509 CONVERT_ARG_CHECKED(String, sub, 0);
2510 CONVERT_ARG_CHECKED(String, pat, 1);
2511
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002512 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002513 uint32_t start_index;
2514 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2515
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002516 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002517 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002518
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002519 if (start_index + pat_length > sub_length) {
2520 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002521 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002522
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002523 if (pat_length == 0) {
2524 return Smi::FromInt(start_index);
2525 }
2526
2527 if (!sub->IsFlat()) {
2528 FlattenString(sub);
2529 }
2530
2531 if (pat_length == 1) {
2532 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2533 if (sub->IsAsciiRepresentation()) {
2534 uc16 pchar = pat->Get(0);
2535 if (pchar > String::kMaxAsciiCharCode) {
2536 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002538 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2539 static_cast<char>(pat->Get(0)),
2540 start_index));
2541 } else {
2542 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2543 pat->Get(0),
2544 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002545 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002546 }
2547
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002548 if (!pat->IsFlat()) {
2549 FlattenString(pat);
2550 }
2551
2552 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2553
2554 int position = -1;
2555
2556 if (pat->IsAsciiRepresentation()) {
2557 Vector<const char> pat_vector = pat->ToAsciiVector();
2558 if (sub->IsAsciiRepresentation()) {
2559 position = StringMatchBackwards(sub->ToAsciiVector(),
2560 pat_vector,
2561 start_index);
2562 } else {
2563 position = StringMatchBackwards(sub->ToUC16Vector(),
2564 pat_vector,
2565 start_index);
2566 }
2567 } else {
2568 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2569 if (sub->IsAsciiRepresentation()) {
2570 position = StringMatchBackwards(sub->ToAsciiVector(),
2571 pat_vector,
2572 start_index);
2573 } else {
2574 position = StringMatchBackwards(sub->ToUC16Vector(),
2575 pat_vector,
2576 start_index);
2577 }
2578 }
2579
2580 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002581}
2582
2583
2584static Object* Runtime_StringLocaleCompare(Arguments args) {
2585 NoHandleAllocation ha;
2586 ASSERT(args.length() == 2);
2587
2588 CONVERT_CHECKED(String, str1, args[0]);
2589 CONVERT_CHECKED(String, str2, args[1]);
2590
2591 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002592 int str1_length = str1->length();
2593 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002594
2595 // Decide trivial cases without flattening.
2596 if (str1_length == 0) {
2597 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2598 return Smi::FromInt(-str2_length);
2599 } else {
2600 if (str2_length == 0) return Smi::FromInt(str1_length);
2601 }
2602
2603 int end = str1_length < str2_length ? str1_length : str2_length;
2604
2605 // No need to flatten if we are going to find the answer on the first
2606 // character. At this point we know there is at least one character
2607 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002608 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002609 if (d != 0) return Smi::FromInt(d);
2610
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002611 str1->TryFlattenIfNotFlat();
2612 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002613
2614 static StringInputBuffer buf1;
2615 static StringInputBuffer buf2;
2616
2617 buf1.Reset(str1);
2618 buf2.Reset(str2);
2619
2620 for (int i = 0; i < end; i++) {
2621 uint16_t char1 = buf1.GetNext();
2622 uint16_t char2 = buf2.GetNext();
2623 if (char1 != char2) return Smi::FromInt(char1 - char2);
2624 }
2625
2626 return Smi::FromInt(str1_length - str2_length);
2627}
2628
2629
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002630static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002631 NoHandleAllocation ha;
2632 ASSERT(args.length() == 3);
2633
2634 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002635 Object* from = args[1];
2636 Object* to = args[2];
2637 int start, end;
2638 // We have a fast integer-only case here to avoid a conversion to double in
2639 // the common case where from and to are Smis.
2640 if (from->IsSmi() && to->IsSmi()) {
2641 start = Smi::cast(from)->value();
2642 end = Smi::cast(to)->value();
2643 } else {
2644 CONVERT_DOUBLE_CHECKED(from_number, from);
2645 CONVERT_DOUBLE_CHECKED(to_number, to);
2646 start = FastD2I(from_number);
2647 end = FastD2I(to_number);
2648 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002649 RUNTIME_ASSERT(end >= start);
2650 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002651 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002652 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002653 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002654}
2655
2656
ager@chromium.org41826e72009-03-30 13:30:57 +00002657static Object* Runtime_StringMatch(Arguments args) {
2658 ASSERT_EQ(3, args.length());
2659
2660 CONVERT_ARG_CHECKED(String, subject, 0);
2661 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2662 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2663 HandleScope handles;
2664
2665 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2666
2667 if (match.is_null()) {
2668 return Failure::Exception();
2669 }
2670 if (match->IsNull()) {
2671 return Heap::null_value();
2672 }
2673 int length = subject->length();
2674
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002675 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002676 ZoneList<int> offsets(8);
2677 do {
2678 int start;
2679 int end;
2680 {
2681 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002682 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002683 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2684 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2685 }
2686 offsets.Add(start);
2687 offsets.Add(end);
2688 int index = start < end ? end : end + 1;
2689 if (index > length) break;
2690 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2691 if (match.is_null()) {
2692 return Failure::Exception();
2693 }
2694 } while (!match->IsNull());
2695 int matches = offsets.length() / 2;
2696 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2697 for (int i = 0; i < matches ; i++) {
2698 int from = offsets.at(i * 2);
2699 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002700 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002701 }
2702 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2703 result->set_length(Smi::FromInt(matches));
2704 return *result;
2705}
2706
2707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708static Object* Runtime_NumberToRadixString(Arguments args) {
2709 NoHandleAllocation ha;
2710 ASSERT(args.length() == 2);
2711
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002712 // Fast case where the result is a one character string.
2713 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2714 int value = Smi::cast(args[0])->value();
2715 int radix = Smi::cast(args[1])->value();
2716 if (value >= 0 && value < radix) {
2717 RUNTIME_ASSERT(radix <= 36);
2718 // Character array used for conversion.
2719 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2720 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2721 }
2722 }
2723
2724 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002725 CONVERT_DOUBLE_CHECKED(value, args[0]);
2726 if (isnan(value)) {
2727 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2728 }
2729 if (isinf(value)) {
2730 if (value < 0) {
2731 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2732 }
2733 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2734 }
2735 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2736 int radix = FastD2I(radix_number);
2737 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2738 char* str = DoubleToRadixCString(value, radix);
2739 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2740 DeleteArray(str);
2741 return result;
2742}
2743
2744
2745static Object* Runtime_NumberToFixed(Arguments args) {
2746 NoHandleAllocation ha;
2747 ASSERT(args.length() == 2);
2748
2749 CONVERT_DOUBLE_CHECKED(value, args[0]);
2750 if (isnan(value)) {
2751 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2752 }
2753 if (isinf(value)) {
2754 if (value < 0) {
2755 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2756 }
2757 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2758 }
2759 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2760 int f = FastD2I(f_number);
2761 RUNTIME_ASSERT(f >= 0);
2762 char* str = DoubleToFixedCString(value, f);
2763 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2764 DeleteArray(str);
2765 return res;
2766}
2767
2768
2769static Object* Runtime_NumberToExponential(Arguments args) {
2770 NoHandleAllocation ha;
2771 ASSERT(args.length() == 2);
2772
2773 CONVERT_DOUBLE_CHECKED(value, args[0]);
2774 if (isnan(value)) {
2775 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2776 }
2777 if (isinf(value)) {
2778 if (value < 0) {
2779 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2780 }
2781 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2782 }
2783 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2784 int f = FastD2I(f_number);
2785 RUNTIME_ASSERT(f >= -1 && f <= 20);
2786 char* str = DoubleToExponentialCString(value, f);
2787 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2788 DeleteArray(str);
2789 return res;
2790}
2791
2792
2793static Object* Runtime_NumberToPrecision(Arguments args) {
2794 NoHandleAllocation ha;
2795 ASSERT(args.length() == 2);
2796
2797 CONVERT_DOUBLE_CHECKED(value, args[0]);
2798 if (isnan(value)) {
2799 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2800 }
2801 if (isinf(value)) {
2802 if (value < 0) {
2803 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2804 }
2805 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2806 }
2807 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2808 int f = FastD2I(f_number);
2809 RUNTIME_ASSERT(f >= 1 && f <= 21);
2810 char* str = DoubleToPrecisionCString(value, f);
2811 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2812 DeleteArray(str);
2813 return res;
2814}
2815
2816
2817// Returns a single character string where first character equals
2818// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002819static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002820 if (index < static_cast<uint32_t>(string->length())) {
2821 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002822 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002823 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002824 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002825 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002826}
2827
2828
2829Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2830 // Handle [] indexing on Strings
2831 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002832 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2833 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002834 }
2835
2836 // Handle [] indexing on String objects
2837 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002838 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2839 Handle<Object> result =
2840 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2841 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002842 }
2843
2844 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002845 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002846 return prototype->GetElement(index);
2847 }
2848
2849 return object->GetElement(index);
2850}
2851
2852
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002853Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2854 HandleScope scope;
2855
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002856 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002857 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002858 Handle<Object> error =
2859 Factory::NewTypeError("non_object_property_load",
2860 HandleVector(args, 2));
2861 return Top::Throw(*error);
2862 }
2863
2864 // Check if the given key is an array index.
2865 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002866 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002867 return GetElementOrCharAt(object, index);
2868 }
2869
2870 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002871 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002872 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002873 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002874 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875 bool has_pending_exception = false;
2876 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002877 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002878 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002879 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002880 }
2881
ager@chromium.org32912102009-01-16 10:38:43 +00002882 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002883 // the element if so.
2884 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002885 return GetElementOrCharAt(object, index);
2886 } else {
2887 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002888 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002889 }
2890}
2891
2892
2893static Object* Runtime_GetProperty(Arguments args) {
2894 NoHandleAllocation ha;
2895 ASSERT(args.length() == 2);
2896
2897 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002898 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002899
2900 return Runtime::GetObjectProperty(object, key);
2901}
2902
2903
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002904// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002905static Object* Runtime_KeyedGetProperty(Arguments args) {
2906 NoHandleAllocation ha;
2907 ASSERT(args.length() == 2);
2908
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002909 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002910 // itself.
2911 //
2912 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002913 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002914 // global proxy object never has properties. This is the case
2915 // because the global proxy object forwards everything to its hidden
2916 // prototype including local lookups.
2917 //
2918 // Additionally, we need to make sure that we do not cache results
2919 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002920 if (args[0]->IsJSObject() &&
2921 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002922 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002923 args[1]->IsString()) {
2924 JSObject* receiver = JSObject::cast(args[0]);
2925 String* key = String::cast(args[1]);
2926 if (receiver->HasFastProperties()) {
2927 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002928 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002929 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2930 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002931 Object* value = receiver->FastPropertyAt(offset);
2932 return value->IsTheHole() ? Heap::undefined_value() : value;
2933 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002934 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002935 LookupResult result;
2936 receiver->LocalLookup(key, &result);
2937 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2938 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002939 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002940 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002941 }
2942 } else {
2943 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002944 StringDictionary* dictionary = receiver->property_dictionary();
2945 int entry = dictionary->FindEntry(key);
2946 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002947 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002948 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002949 if (!receiver->IsGlobalObject()) return value;
2950 value = JSGlobalPropertyCell::cast(value)->value();
2951 if (!value->IsTheHole()) return value;
2952 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002953 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002954 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002955 } else if (args[0]->IsString() && args[1]->IsSmi()) {
2956 // Fast case for string indexing using [] with a smi index.
2957 HandleScope scope;
2958 Handle<String> str = args.at<String>(0);
2959 int index = Smi::cast(args[1])->value();
2960 Handle<Object> result = GetCharAt(str, index);
2961 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002962 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002963
2964 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002965 return Runtime::GetObjectProperty(args.at<Object>(0),
2966 args.at<Object>(1));
2967}
2968
2969
ager@chromium.org5c838252010-02-19 08:53:10 +00002970static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
2971 ASSERT(args.length() == 5);
2972 HandleScope scope;
2973 CONVERT_ARG_CHECKED(JSObject, obj, 0);
2974 CONVERT_CHECKED(String, name, args[1]);
2975 CONVERT_CHECKED(Smi, flag_setter, args[2]);
2976 CONVERT_CHECKED(JSFunction, fun, args[3]);
2977 CONVERT_CHECKED(Smi, flag_attr, args[4]);
2978 int unchecked = flag_attr->value();
2979 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2980 RUNTIME_ASSERT(!obj->IsNull());
2981 LookupResult result;
2982 obj->LocalLookupRealNamedProperty(name, &result);
2983
2984 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
2985 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
2986 // delete it to avoid running into trouble in DefineAccessor, which
2987 // handles this incorrectly if the property is readonly (does nothing)
2988 if (result.IsProperty() &&
2989 (result.type() == FIELD || result.type() == NORMAL
2990 || result.type() == CONSTANT_FUNCTION)) {
2991 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
2992 }
2993 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
2994}
2995
2996static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
2997 ASSERT(args.length() == 4);
2998 HandleScope scope;
2999 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3000 CONVERT_ARG_CHECKED(String, name, 1);
3001 Handle<Object> obj_value = args.at<Object>(2);
3002
3003 CONVERT_CHECKED(Smi, flag, args[3]);
3004 int unchecked = flag->value();
3005 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3006
3007 LookupResult result;
3008 js_object->LocalLookupRealNamedProperty(*name, &result);
3009
3010 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3011
3012 // Take special care when attributes are different and there is already
3013 // a property. For simplicity we normalize the property which enables us
3014 // to not worry about changing the instance_descriptor and creating a new
3015 // map. The current version of SetObjectProperty does not handle attributes
3016 // correctly in the case where a property is a field and is reset with
3017 // new attributes.
3018 if (result.IsProperty() && attr != result.GetAttributes()) {
3019 // New attributes - normalize to avoid writing to instance descriptor
3020 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3021 // Use IgnoreAttributes version since a readonly property may be
3022 // overridden and SetProperty does not allow this.
3023 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3024 *obj_value,
3025 attr);
3026 }
3027 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3028}
3029
3030
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003031Object* Runtime::SetObjectProperty(Handle<Object> object,
3032 Handle<Object> key,
3033 Handle<Object> value,
3034 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003035 HandleScope scope;
3036
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003037 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003038 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003039 Handle<Object> error =
3040 Factory::NewTypeError("non_object_property_store",
3041 HandleVector(args, 2));
3042 return Top::Throw(*error);
3043 }
3044
3045 // If the object isn't a JavaScript object, we ignore the store.
3046 if (!object->IsJSObject()) return *value;
3047
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003048 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3049
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003050 // Check if the given key is an array index.
3051 uint32_t index;
3052 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003053 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3054 // of a string using [] notation. We need to support this too in
3055 // JavaScript.
3056 // In the case of a String object we just need to redirect the assignment to
3057 // the underlying string if the index is in range. Since the underlying
3058 // string does nothing with the assignment then we can ignore such
3059 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003060 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003061 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003062 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003063
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003064 Handle<Object> result = SetElement(js_object, index, value);
3065 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003066 return *value;
3067 }
3068
3069 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003070 Handle<Object> result;
3071 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003072 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003073 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003074 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003075 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003076 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003077 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003078 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003079 return *value;
3080 }
3081
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003082 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003083 bool has_pending_exception = false;
3084 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3085 if (has_pending_exception) return Failure::Exception();
3086 Handle<String> name = Handle<String>::cast(converted);
3087
3088 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003089 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003090 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003091 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003092 }
3093}
3094
3095
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003096Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3097 Handle<Object> key,
3098 Handle<Object> value,
3099 PropertyAttributes attr) {
3100 HandleScope scope;
3101
3102 // Check if the given key is an array index.
3103 uint32_t index;
3104 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003105 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3106 // of a string using [] notation. We need to support this too in
3107 // JavaScript.
3108 // In the case of a String object we just need to redirect the assignment to
3109 // the underlying string if the index is in range. Since the underlying
3110 // string does nothing with the assignment then we can ignore such
3111 // assignments.
3112 if (js_object->IsStringObjectWithCharacterAt(index)) {
3113 return *value;
3114 }
3115
3116 return js_object->SetElement(index, *value);
3117 }
3118
3119 if (key->IsString()) {
3120 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003121 return js_object->SetElement(index, *value);
3122 } else {
3123 Handle<String> key_string = Handle<String>::cast(key);
3124 key_string->TryFlattenIfNotFlat();
3125 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3126 *value,
3127 attr);
3128 }
3129 }
3130
3131 // Call-back into JavaScript to convert the key to a string.
3132 bool has_pending_exception = false;
3133 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3134 if (has_pending_exception) return Failure::Exception();
3135 Handle<String> name = Handle<String>::cast(converted);
3136
3137 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003138 return js_object->SetElement(index, *value);
3139 } else {
3140 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3141 }
3142}
3143
3144
ager@chromium.orge2902be2009-06-08 12:21:35 +00003145Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3146 Handle<Object> key) {
3147 HandleScope scope;
3148
3149 // Check if the given key is an array index.
3150 uint32_t index;
3151 if (Array::IndexFromObject(*key, &index)) {
3152 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3153 // characters of a string using [] notation. In the case of a
3154 // String object we just need to redirect the deletion to the
3155 // underlying string if the index is in range. Since the
3156 // underlying string does nothing with the deletion, we can ignore
3157 // such deletions.
3158 if (js_object->IsStringObjectWithCharacterAt(index)) {
3159 return Heap::true_value();
3160 }
3161
3162 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3163 }
3164
3165 Handle<String> key_string;
3166 if (key->IsString()) {
3167 key_string = Handle<String>::cast(key);
3168 } else {
3169 // Call-back into JavaScript to convert the key to a string.
3170 bool has_pending_exception = false;
3171 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3172 if (has_pending_exception) return Failure::Exception();
3173 key_string = Handle<String>::cast(converted);
3174 }
3175
3176 key_string->TryFlattenIfNotFlat();
3177 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3178}
3179
3180
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003181static Object* Runtime_SetProperty(Arguments args) {
3182 NoHandleAllocation ha;
3183 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3184
3185 Handle<Object> object = args.at<Object>(0);
3186 Handle<Object> key = args.at<Object>(1);
3187 Handle<Object> value = args.at<Object>(2);
3188
3189 // Compute attributes.
3190 PropertyAttributes attributes = NONE;
3191 if (args.length() == 4) {
3192 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003193 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003194 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003195 RUNTIME_ASSERT(
3196 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3197 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003198 }
3199 return Runtime::SetObjectProperty(object, key, value, attributes);
3200}
3201
3202
3203// Set a local property, even if it is READ_ONLY. If the property does not
3204// exist, it will be added with attributes NONE.
3205static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
3206 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003207 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003208 CONVERT_CHECKED(JSObject, object, args[0]);
3209 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003210 // Compute attributes.
3211 PropertyAttributes attributes = NONE;
3212 if (args.length() == 4) {
3213 CONVERT_CHECKED(Smi, value_obj, args[3]);
3214 int unchecked_value = value_obj->value();
3215 // Only attribute bits should be set.
3216 RUNTIME_ASSERT(
3217 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3218 attributes = static_cast<PropertyAttributes>(unchecked_value);
3219 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003220
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003221 return object->
3222 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003223}
3224
3225
3226static Object* Runtime_DeleteProperty(Arguments args) {
3227 NoHandleAllocation ha;
3228 ASSERT(args.length() == 2);
3229
3230 CONVERT_CHECKED(JSObject, object, args[0]);
3231 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00003232 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003233}
3234
3235
ager@chromium.org9085a012009-05-11 19:22:57 +00003236static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
3237 Handle<String> key) {
3238 if (object->HasLocalProperty(*key)) return Heap::true_value();
3239 // Handle hidden prototypes. If there's a hidden prototype above this thing
3240 // then we have to check it for properties, because they are supposed to
3241 // look like they are on this object.
3242 Handle<Object> proto(object->GetPrototype());
3243 if (proto->IsJSObject() &&
3244 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
3245 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
3246 }
3247 return Heap::false_value();
3248}
3249
3250
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003251static Object* Runtime_HasLocalProperty(Arguments args) {
3252 NoHandleAllocation ha;
3253 ASSERT(args.length() == 2);
3254 CONVERT_CHECKED(String, key, args[1]);
3255
ager@chromium.org9085a012009-05-11 19:22:57 +00003256 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003257 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00003258 if (obj->IsJSObject()) {
3259 JSObject* object = JSObject::cast(obj);
3260 // Fast case - no interceptors.
3261 if (object->HasRealNamedProperty(key)) return Heap::true_value();
3262 // Slow case. Either it's not there or we have an interceptor. We should
3263 // have handles for this kind of deal.
3264 HandleScope scope;
3265 return HasLocalPropertyImplementation(Handle<JSObject>(object),
3266 Handle<String>(key));
3267 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003268 // Well, there is one exception: Handle [] on strings.
3269 uint32_t index;
3270 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00003271 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003272 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003273 return Heap::true_value();
3274 }
3275 }
3276 return Heap::false_value();
3277}
3278
3279
3280static Object* Runtime_HasProperty(Arguments args) {
3281 NoHandleAllocation na;
3282 ASSERT(args.length() == 2);
3283
3284 // Only JS objects can have properties.
3285 if (args[0]->IsJSObject()) {
3286 JSObject* object = JSObject::cast(args[0]);
3287 CONVERT_CHECKED(String, key, args[1]);
3288 if (object->HasProperty(key)) return Heap::true_value();
3289 }
3290 return Heap::false_value();
3291}
3292
3293
3294static Object* Runtime_HasElement(Arguments args) {
3295 NoHandleAllocation na;
3296 ASSERT(args.length() == 2);
3297
3298 // Only JS objects can have elements.
3299 if (args[0]->IsJSObject()) {
3300 JSObject* object = JSObject::cast(args[0]);
3301 CONVERT_CHECKED(Smi, index_obj, args[1]);
3302 uint32_t index = index_obj->value();
3303 if (object->HasElement(index)) return Heap::true_value();
3304 }
3305 return Heap::false_value();
3306}
3307
3308
3309static Object* Runtime_IsPropertyEnumerable(Arguments args) {
3310 NoHandleAllocation ha;
3311 ASSERT(args.length() == 2);
3312
3313 CONVERT_CHECKED(JSObject, object, args[0]);
3314 CONVERT_CHECKED(String, key, args[1]);
3315
3316 uint32_t index;
3317 if (key->AsArrayIndex(&index)) {
3318 return Heap::ToBoolean(object->HasElement(index));
3319 }
3320
ager@chromium.org870a0b62008-11-04 11:43:05 +00003321 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
3322 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003323}
3324
3325
3326static Object* Runtime_GetPropertyNames(Arguments args) {
3327 HandleScope scope;
3328 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003329 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330 return *GetKeysFor(object);
3331}
3332
3333
3334// Returns either a FixedArray as Runtime_GetPropertyNames,
3335// or, if the given object has an enum cache that contains
3336// all enumerable properties of the object and its prototypes
3337// have none, the map of the object. This is used to speed up
3338// the check for deletions during a for-in.
3339static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3340 ASSERT(args.length() == 1);
3341
3342 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3343
3344 if (raw_object->IsSimpleEnum()) return raw_object->map();
3345
3346 HandleScope scope;
3347 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003348 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3349 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003350
3351 // Test again, since cache may have been built by preceding call.
3352 if (object->IsSimpleEnum()) return object->map();
3353
3354 return *content;
3355}
3356
3357
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003358// Find the length of the prototype chain that is to to handled as one. If a
3359// prototype object is hidden it is to be viewed as part of the the object it
3360// is prototype for.
3361static int LocalPrototypeChainLength(JSObject* obj) {
3362 int count = 1;
3363 Object* proto = obj->GetPrototype();
3364 while (proto->IsJSObject() &&
3365 JSObject::cast(proto)->map()->is_hidden_prototype()) {
3366 count++;
3367 proto = JSObject::cast(proto)->GetPrototype();
3368 }
3369 return count;
3370}
3371
3372
3373// Return the names of the local named properties.
3374// args[0]: object
3375static Object* Runtime_GetLocalPropertyNames(Arguments args) {
3376 HandleScope scope;
3377 ASSERT(args.length() == 1);
3378 if (!args[0]->IsJSObject()) {
3379 return Heap::undefined_value();
3380 }
3381 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3382
3383 // Skip the global proxy as it has no properties and always delegates to the
3384 // real global object.
3385 if (obj->IsJSGlobalProxy()) {
3386 // Only collect names if access is permitted.
3387 if (obj->IsAccessCheckNeeded() &&
3388 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
3389 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
3390 return *Factory::NewJSArray(0);
3391 }
3392 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
3393 }
3394
3395 // Find the number of objects making up this.
3396 int length = LocalPrototypeChainLength(*obj);
3397
3398 // Find the number of local properties for each of the objects.
3399 int* local_property_count = NewArray<int>(length);
3400 int total_property_count = 0;
3401 Handle<JSObject> jsproto = obj;
3402 for (int i = 0; i < length; i++) {
3403 // Only collect names if access is permitted.
3404 if (jsproto->IsAccessCheckNeeded() &&
3405 !Top::MayNamedAccess(*jsproto,
3406 Heap::undefined_value(),
3407 v8::ACCESS_KEYS)) {
3408 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
3409 return *Factory::NewJSArray(0);
3410 }
3411 int n;
3412 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
3413 local_property_count[i] = n;
3414 total_property_count += n;
3415 if (i < length - 1) {
3416 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3417 }
3418 }
3419
3420 // Allocate an array with storage for all the property names.
3421 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
3422
3423 // Get the property names.
3424 jsproto = obj;
3425 int proto_with_hidden_properties = 0;
3426 for (int i = 0; i < length; i++) {
3427 jsproto->GetLocalPropertyNames(*names,
3428 i == 0 ? 0 : local_property_count[i - 1]);
3429 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
3430 proto_with_hidden_properties++;
3431 }
3432 if (i < length - 1) {
3433 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3434 }
3435 }
3436
3437 // Filter out name of hidden propeties object.
3438 if (proto_with_hidden_properties > 0) {
3439 Handle<FixedArray> old_names = names;
3440 names = Factory::NewFixedArray(
3441 names->length() - proto_with_hidden_properties);
3442 int dest_pos = 0;
3443 for (int i = 0; i < total_property_count; i++) {
3444 Object* name = old_names->get(i);
3445 if (name == Heap::hidden_symbol()) {
3446 continue;
3447 }
3448 names->set(dest_pos++, name);
3449 }
3450 }
3451
3452 DeleteArray(local_property_count);
3453 return *Factory::NewJSArrayWithElements(names);
3454}
3455
3456
3457// Return the names of the local indexed properties.
3458// args[0]: object
3459static Object* Runtime_GetLocalElementNames(Arguments args) {
3460 HandleScope scope;
3461 ASSERT(args.length() == 1);
3462 if (!args[0]->IsJSObject()) {
3463 return Heap::undefined_value();
3464 }
3465 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3466
3467 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
3468 Handle<FixedArray> names = Factory::NewFixedArray(n);
3469 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
3470 return *Factory::NewJSArrayWithElements(names);
3471}
3472
3473
3474// Return information on whether an object has a named or indexed interceptor.
3475// args[0]: object
3476static Object* Runtime_GetInterceptorInfo(Arguments args) {
3477 HandleScope scope;
3478 ASSERT(args.length() == 1);
3479 if (!args[0]->IsJSObject()) {
3480 return Smi::FromInt(0);
3481 }
3482 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3483
3484 int result = 0;
3485 if (obj->HasNamedInterceptor()) result |= 2;
3486 if (obj->HasIndexedInterceptor()) result |= 1;
3487
3488 return Smi::FromInt(result);
3489}
3490
3491
3492// Return property names from named interceptor.
3493// args[0]: object
3494static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
3495 HandleScope scope;
3496 ASSERT(args.length() == 1);
3497 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3498
3499 if (obj->HasNamedInterceptor()) {
3500 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
3501 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3502 }
3503 return Heap::undefined_value();
3504}
3505
3506
3507// Return element names from indexed interceptor.
3508// args[0]: object
3509static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
3510 HandleScope scope;
3511 ASSERT(args.length() == 1);
3512 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3513
3514 if (obj->HasIndexedInterceptor()) {
3515 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
3516 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3517 }
3518 return Heap::undefined_value();
3519}
3520
3521
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003522static Object* Runtime_LocalKeys(Arguments args) {
3523 ASSERT_EQ(args.length(), 1);
3524 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3525 HandleScope scope;
3526 Handle<JSObject> object(raw_object);
3527 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3528 LOCAL_ONLY);
3529 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3530 // property array and since the result is mutable we have to create
3531 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003532 int length = contents->length();
3533 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3534 for (int i = 0; i < length; i++) {
3535 Object* entry = contents->get(i);
3536 if (entry->IsString()) {
3537 copy->set(i, entry);
3538 } else {
3539 ASSERT(entry->IsNumber());
3540 HandleScope scope;
3541 Handle<Object> entry_handle(entry);
3542 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3543 copy->set(i, *entry_str);
3544 }
3545 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003546 return *Factory::NewJSArrayWithElements(copy);
3547}
3548
3549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003550static Object* Runtime_GetArgumentsProperty(Arguments args) {
3551 NoHandleAllocation ha;
3552 ASSERT(args.length() == 1);
3553
3554 // Compute the frame holding the arguments.
3555 JavaScriptFrameIterator it;
3556 it.AdvanceToArgumentsFrame();
3557 JavaScriptFrame* frame = it.frame();
3558
3559 // Get the actual number of provided arguments.
3560 const uint32_t n = frame->GetProvidedParametersCount();
3561
3562 // Try to convert the key to an index. If successful and within
3563 // index return the the argument from the frame.
3564 uint32_t index;
3565 if (Array::IndexFromObject(args[0], &index) && index < n) {
3566 return frame->GetParameter(index);
3567 }
3568
3569 // Convert the key to a string.
3570 HandleScope scope;
3571 bool exception = false;
3572 Handle<Object> converted =
3573 Execution::ToString(args.at<Object>(0), &exception);
3574 if (exception) return Failure::Exception();
3575 Handle<String> key = Handle<String>::cast(converted);
3576
3577 // Try to convert the string key into an array index.
3578 if (key->AsArrayIndex(&index)) {
3579 if (index < n) {
3580 return frame->GetParameter(index);
3581 } else {
3582 return Top::initial_object_prototype()->GetElement(index);
3583 }
3584 }
3585
3586 // Handle special arguments properties.
3587 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3588 if (key->Equals(Heap::callee_symbol())) return frame->function();
3589
3590 // Lookup in the initial Object.prototype object.
3591 return Top::initial_object_prototype()->GetProperty(*key);
3592}
3593
3594
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003595static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003596 HandleScope scope;
3597
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003598 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003599 Handle<Object> object = args.at<Object>(0);
3600 if (object->IsJSObject()) {
3601 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00003602 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
3603 js_object->TransformToFastProperties(0);
3604 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003605 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003606 return *object;
3607}
3608
3609
3610static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003611 HandleScope scope;
3612
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003613 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003614 Handle<Object> object = args.at<Object>(0);
3615 if (object->IsJSObject()) {
3616 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003617 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003618 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003619 return *object;
3620}
3621
3622
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003623static Object* Runtime_ToBool(Arguments args) {
3624 NoHandleAllocation ha;
3625 ASSERT(args.length() == 1);
3626
3627 return args[0]->ToBoolean();
3628}
3629
3630
3631// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3632// Possible optimizations: put the type string into the oddballs.
3633static Object* Runtime_Typeof(Arguments args) {
3634 NoHandleAllocation ha;
3635
3636 Object* obj = args[0];
3637 if (obj->IsNumber()) return Heap::number_symbol();
3638 HeapObject* heap_obj = HeapObject::cast(obj);
3639
3640 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003641 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003642
3643 InstanceType instance_type = heap_obj->map()->instance_type();
3644 if (instance_type < FIRST_NONSTRING_TYPE) {
3645 return Heap::string_symbol();
3646 }
3647
3648 switch (instance_type) {
3649 case ODDBALL_TYPE:
3650 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3651 return Heap::boolean_symbol();
3652 }
3653 if (heap_obj->IsNull()) {
3654 return Heap::object_symbol();
3655 }
3656 ASSERT(heap_obj->IsUndefined());
3657 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003658 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003659 return Heap::function_symbol();
3660 default:
3661 // For any kind of object not handled above, the spec rule for
3662 // host objects gives that it is okay to return "object"
3663 return Heap::object_symbol();
3664 }
3665}
3666
3667
3668static Object* Runtime_StringToNumber(Arguments args) {
3669 NoHandleAllocation ha;
3670 ASSERT(args.length() == 1);
3671 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003672 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003673 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3674}
3675
3676
3677static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3678 NoHandleAllocation ha;
3679 ASSERT(args.length() == 1);
3680
3681 CONVERT_CHECKED(JSArray, codes, args[0]);
3682 int length = Smi::cast(codes->length())->value();
3683
3684 // Check if the string can be ASCII.
3685 int i;
3686 for (i = 0; i < length; i++) {
3687 Object* element = codes->GetElement(i);
3688 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3689 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3690 break;
3691 }
3692
3693 Object* object = NULL;
3694 if (i == length) { // The string is ASCII.
3695 object = Heap::AllocateRawAsciiString(length);
3696 } else { // The string is not ASCII.
3697 object = Heap::AllocateRawTwoByteString(length);
3698 }
3699
3700 if (object->IsFailure()) return object;
3701 String* result = String::cast(object);
3702 for (int i = 0; i < length; i++) {
3703 Object* element = codes->GetElement(i);
3704 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003705 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003706 }
3707 return result;
3708}
3709
3710
3711// kNotEscaped is generated by the following:
3712//
3713// #!/bin/perl
3714// for (my $i = 0; $i < 256; $i++) {
3715// print "\n" if $i % 16 == 0;
3716// my $c = chr($i);
3717// my $escaped = 1;
3718// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3719// print $escaped ? "0, " : "1, ";
3720// }
3721
3722
3723static bool IsNotEscaped(uint16_t character) {
3724 // Only for 8 bit characters, the rest are always escaped (in a different way)
3725 ASSERT(character < 256);
3726 static const char kNotEscaped[256] = {
3727 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3728 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3729 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3730 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3731 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3732 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3733 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3734 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3735 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3736 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3737 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3738 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3739 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3740 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3741 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3742 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3743 };
3744 return kNotEscaped[character] != 0;
3745}
3746
3747
3748static Object* Runtime_URIEscape(Arguments args) {
3749 const char hex_chars[] = "0123456789ABCDEF";
3750 NoHandleAllocation ha;
3751 ASSERT(args.length() == 1);
3752 CONVERT_CHECKED(String, source, args[0]);
3753
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003754 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003755
3756 int escaped_length = 0;
3757 int length = source->length();
3758 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003759 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003760 buffer->Reset(source);
3761 while (buffer->has_more()) {
3762 uint16_t character = buffer->GetNext();
3763 if (character >= 256) {
3764 escaped_length += 6;
3765 } else if (IsNotEscaped(character)) {
3766 escaped_length++;
3767 } else {
3768 escaped_length += 3;
3769 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003770 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003771 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003772 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003773 Top::context()->mark_out_of_memory();
3774 return Failure::OutOfMemoryException();
3775 }
3776 }
3777 }
3778 // No length change implies no change. Return original string if no change.
3779 if (escaped_length == length) {
3780 return source;
3781 }
3782 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3783 if (o->IsFailure()) return o;
3784 String* destination = String::cast(o);
3785 int dest_position = 0;
3786
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003787 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003788 buffer->Rewind();
3789 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003790 uint16_t chr = buffer->GetNext();
3791 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003792 destination->Set(dest_position, '%');
3793 destination->Set(dest_position+1, 'u');
3794 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3795 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3796 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3797 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003798 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003799 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003800 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003801 dest_position++;
3802 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003803 destination->Set(dest_position, '%');
3804 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3805 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003806 dest_position += 3;
3807 }
3808 }
3809 return destination;
3810}
3811
3812
3813static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3814 static const signed char kHexValue['g'] = {
3815 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3816 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3817 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3818 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3819 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3820 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3821 -1, 10, 11, 12, 13, 14, 15 };
3822
3823 if (character1 > 'f') return -1;
3824 int hi = kHexValue[character1];
3825 if (hi == -1) return -1;
3826 if (character2 > 'f') return -1;
3827 int lo = kHexValue[character2];
3828 if (lo == -1) return -1;
3829 return (hi << 4) + lo;
3830}
3831
3832
ager@chromium.org870a0b62008-11-04 11:43:05 +00003833static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003834 int i,
3835 int length,
3836 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003837 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003838 int32_t hi = 0;
3839 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003840 if (character == '%' &&
3841 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003842 source->Get(i + 1) == 'u' &&
3843 (hi = TwoDigitHex(source->Get(i + 2),
3844 source->Get(i + 3))) != -1 &&
3845 (lo = TwoDigitHex(source->Get(i + 4),
3846 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847 *step = 6;
3848 return (hi << 8) + lo;
3849 } else if (character == '%' &&
3850 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003851 (lo = TwoDigitHex(source->Get(i + 1),
3852 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003853 *step = 3;
3854 return lo;
3855 } else {
3856 *step = 1;
3857 return character;
3858 }
3859}
3860
3861
3862static Object* Runtime_URIUnescape(Arguments args) {
3863 NoHandleAllocation ha;
3864 ASSERT(args.length() == 1);
3865 CONVERT_CHECKED(String, source, args[0]);
3866
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003867 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003868
3869 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003870 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003871
3872 int unescaped_length = 0;
3873 for (int i = 0; i < length; unescaped_length++) {
3874 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003875 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003876 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003877 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003878 i += step;
3879 }
3880
3881 // No length change implies no change. Return original string if no change.
3882 if (unescaped_length == length)
3883 return source;
3884
3885 Object* o = ascii ?
3886 Heap::AllocateRawAsciiString(unescaped_length) :
3887 Heap::AllocateRawTwoByteString(unescaped_length);
3888 if (o->IsFailure()) return o;
3889 String* destination = String::cast(o);
3890
3891 int dest_position = 0;
3892 for (int i = 0; i < length; dest_position++) {
3893 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003894 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 i += step;
3896 }
3897 return destination;
3898}
3899
3900
3901static Object* Runtime_StringParseInt(Arguments args) {
3902 NoHandleAllocation ha;
3903
3904 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003905 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003907 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003908
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003909 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003910 int i;
3911
3912 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003913 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003914 if (i == len) return Heap::nan_value();
3915
3916 // Compute the sign (default to +).
3917 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003918 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003919 sign = -1;
3920 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003921 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003922 i++;
3923 }
3924
3925 // Compute the radix if 0.
3926 if (radix == 0) {
3927 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003928 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003929 radix = 8;
3930 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003931 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003932 if (c == 'x' || c == 'X') {
3933 radix = 16;
3934 i += 2;
3935 }
3936 }
3937 }
3938 } else if (radix == 16) {
3939 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003940 if (i + 1 < len && s->Get(i) == '0') {
3941 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003942 if (c == 'x' || c == 'X') i += 2;
3943 }
3944 }
3945
3946 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3947 double value;
3948 int end_index = StringToInt(s, i, radix, &value);
3949 if (end_index != i) {
3950 return Heap::NumberFromDouble(sign * value);
3951 }
3952 return Heap::nan_value();
3953}
3954
3955
3956static Object* Runtime_StringParseFloat(Arguments args) {
3957 NoHandleAllocation ha;
3958 CONVERT_CHECKED(String, str, args[0]);
3959
3960 // ECMA-262 section 15.1.2.3, empty string is NaN
3961 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3962
3963 // Create a number object from the value.
3964 return Heap::NumberFromDouble(value);
3965}
3966
3967
3968static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3969static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3970
3971
3972template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003973static Object* ConvertCaseHelper(String* s,
3974 int length,
3975 int input_string_length,
3976 unibrow::Mapping<Converter, 128>* mapping) {
3977 // We try this twice, once with the assumption that the result is no longer
3978 // than the input and, if that assumption breaks, again with the exact
3979 // length. This may not be pretty, but it is nicer than what was here before
3980 // and I hereby claim my vaffel-is.
3981 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003982 // Allocate the resulting string.
3983 //
3984 // NOTE: This assumes that the upper/lower case of an ascii
3985 // character is also ascii. This is currently the case, but it
3986 // might break in the future if we implement more context and locale
3987 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003988 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003989 ? Heap::AllocateRawAsciiString(length)
3990 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003991 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003992 String* result = String::cast(o);
3993 bool has_changed_character = false;
3994
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003995 // Convert all characters to upper case, assuming that they will fit
3996 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003997 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003998 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003999 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004000 // We can assume that the string is not empty
4001 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004002 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004003 bool has_next = buffer->has_more();
4004 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004005 int char_length = mapping->get(current, next, chars);
4006 if (char_length == 0) {
4007 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004008 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004009 i++;
4010 } else if (char_length == 1) {
4011 // Common case: converting the letter resulted in one character.
4012 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004013 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004014 has_changed_character = true;
4015 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004016 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004017 // We've assumed that the result would be as long as the
4018 // input but here is a character that converts to several
4019 // characters. No matter, we calculate the exact length
4020 // of the result and try the whole thing again.
4021 //
4022 // Note that this leaves room for optimization. We could just
4023 // memcpy what we already have to the result string. Also,
4024 // the result string is the last object allocated we could
4025 // "realloc" it and probably, in the vast majority of cases,
4026 // extend the existing string to be able to hold the full
4027 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004028 int next_length = 0;
4029 if (has_next) {
4030 next_length = mapping->get(next, 0, chars);
4031 if (next_length == 0) next_length = 1;
4032 }
4033 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004034 while (buffer->has_more()) {
4035 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004036 // NOTE: we use 0 as the next character here because, while
4037 // the next character may affect what a character converts to,
4038 // it does not in any case affect the length of what it convert
4039 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004040 int char_length = mapping->get(current, 0, chars);
4041 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004042 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004043 if (current_length > Smi::kMaxValue) {
4044 Top::context()->mark_out_of_memory();
4045 return Failure::OutOfMemoryException();
4046 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004047 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004048 // Try again with the real length.
4049 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004050 } else {
4051 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004052 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004053 i++;
4054 }
4055 has_changed_character = true;
4056 }
4057 current = next;
4058 }
4059 if (has_changed_character) {
4060 return result;
4061 } else {
4062 // If we didn't actually change anything in doing the conversion
4063 // we simple return the result and let the converted string
4064 // become garbage; there is no reason to keep two identical strings
4065 // alive.
4066 return s;
4067 }
4068}
4069
4070
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004071template <class Converter>
4072static Object* ConvertCase(Arguments args,
4073 unibrow::Mapping<Converter, 128>* mapping) {
4074 NoHandleAllocation ha;
4075
4076 CONVERT_CHECKED(String, s, args[0]);
4077 s->TryFlattenIfNotFlat();
4078
4079 int input_string_length = s->length();
4080 // Assume that the string is not empty; we need this assumption later
4081 if (input_string_length == 0) return s;
4082 int length = input_string_length;
4083
4084 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4085 if (answer->IsSmi()) {
4086 // Retry with correct length.
4087 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4088 }
4089 return answer; // This may be a failure.
4090}
4091
4092
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004093static Object* Runtime_StringToLowerCase(Arguments args) {
4094 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
4095}
4096
4097
4098static Object* Runtime_StringToUpperCase(Arguments args) {
4099 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
4100}
4101
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004102static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
4103 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
4104}
4105
4106static Object* Runtime_StringTrim(Arguments args) {
4107 NoHandleAllocation ha;
4108 ASSERT(args.length() == 3);
4109
4110 CONVERT_CHECKED(String, s, args[0]);
4111 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
4112 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
4113
4114 s->TryFlattenIfNotFlat();
4115 int length = s->length();
4116
4117 int left = 0;
4118 if (trimLeft) {
4119 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
4120 left++;
4121 }
4122 }
4123
4124 int right = length;
4125 if (trimRight) {
4126 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
4127 right--;
4128 }
4129 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004130 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004131}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004132
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00004133bool Runtime::IsUpperCaseChar(uint16_t ch) {
4134 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
4135 int char_length = to_upper_mapping.get(ch, 0, chars);
4136 return char_length == 0;
4137}
4138
4139
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004140static Object* Runtime_NumberToString(Arguments args) {
4141 NoHandleAllocation ha;
4142 ASSERT(args.length() == 1);
4143
4144 Object* number = args[0];
4145 RUNTIME_ASSERT(number->IsNumber());
4146
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004147 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004148}
4149
4150
4151static Object* Runtime_NumberToInteger(Arguments args) {
4152 NoHandleAllocation ha;
4153 ASSERT(args.length() == 1);
4154
4155 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004156 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004157 CONVERT_DOUBLE_CHECKED(number, obj);
4158 return Heap::NumberFromDouble(DoubleToInteger(number));
4159}
4160
4161
4162static Object* Runtime_NumberToJSUint32(Arguments args) {
4163 NoHandleAllocation ha;
4164 ASSERT(args.length() == 1);
4165
4166 Object* obj = args[0];
4167 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
4168 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
4169 return Heap::NumberFromUint32(number);
4170}
4171
4172
4173static Object* Runtime_NumberToJSInt32(Arguments args) {
4174 NoHandleAllocation ha;
4175 ASSERT(args.length() == 1);
4176
4177 Object* obj = args[0];
4178 if (obj->IsSmi()) return obj;
4179 CONVERT_DOUBLE_CHECKED(number, obj);
4180 return Heap::NumberFromInt32(DoubleToInt32(number));
4181}
4182
4183
ager@chromium.org870a0b62008-11-04 11:43:05 +00004184// Converts a Number to a Smi, if possible. Returns NaN if the number is not
4185// a small integer.
4186static Object* Runtime_NumberToSmi(Arguments args) {
4187 NoHandleAllocation ha;
4188 ASSERT(args.length() == 1);
4189
4190 Object* obj = args[0];
4191 if (obj->IsSmi()) {
4192 return obj;
4193 }
4194 if (obj->IsHeapNumber()) {
4195 double value = HeapNumber::cast(obj)->value();
4196 int int_value = FastD2I(value);
4197 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
4198 return Smi::FromInt(int_value);
4199 }
4200 }
4201 return Heap::nan_value();
4202}
4203
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004204
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004205static Object* Runtime_NumberAdd(Arguments args) {
4206 NoHandleAllocation ha;
4207 ASSERT(args.length() == 2);
4208
4209 CONVERT_DOUBLE_CHECKED(x, args[0]);
4210 CONVERT_DOUBLE_CHECKED(y, args[1]);
4211 return Heap::AllocateHeapNumber(x + y);
4212}
4213
4214
4215static Object* Runtime_NumberSub(Arguments args) {
4216 NoHandleAllocation ha;
4217 ASSERT(args.length() == 2);
4218
4219 CONVERT_DOUBLE_CHECKED(x, args[0]);
4220 CONVERT_DOUBLE_CHECKED(y, args[1]);
4221 return Heap::AllocateHeapNumber(x - y);
4222}
4223
4224
4225static Object* Runtime_NumberMul(Arguments args) {
4226 NoHandleAllocation ha;
4227 ASSERT(args.length() == 2);
4228
4229 CONVERT_DOUBLE_CHECKED(x, args[0]);
4230 CONVERT_DOUBLE_CHECKED(y, args[1]);
4231 return Heap::AllocateHeapNumber(x * y);
4232}
4233
4234
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004235static Object* Runtime_NumberUnaryMinus(Arguments args) {
4236 NoHandleAllocation ha;
4237 ASSERT(args.length() == 1);
4238
4239 CONVERT_DOUBLE_CHECKED(x, args[0]);
4240 return Heap::AllocateHeapNumber(-x);
4241}
4242
4243
4244static Object* Runtime_NumberDiv(Arguments args) {
4245 NoHandleAllocation ha;
4246 ASSERT(args.length() == 2);
4247
4248 CONVERT_DOUBLE_CHECKED(x, args[0]);
4249 CONVERT_DOUBLE_CHECKED(y, args[1]);
4250 return Heap::NewNumberFromDouble(x / y);
4251}
4252
4253
4254static Object* Runtime_NumberMod(Arguments args) {
4255 NoHandleAllocation ha;
4256 ASSERT(args.length() == 2);
4257
4258 CONVERT_DOUBLE_CHECKED(x, args[0]);
4259 CONVERT_DOUBLE_CHECKED(y, args[1]);
4260
ager@chromium.org3811b432009-10-28 14:53:37 +00004261 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004262 // NewNumberFromDouble may return a Smi instead of a Number object
4263 return Heap::NewNumberFromDouble(x);
4264}
4265
4266
4267static Object* Runtime_StringAdd(Arguments args) {
4268 NoHandleAllocation ha;
4269 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004270 CONVERT_CHECKED(String, str1, args[0]);
4271 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004272 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004273 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004274}
4275
4276
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004277template<typename sinkchar>
4278static inline void StringBuilderConcatHelper(String* special,
4279 sinkchar* sink,
4280 FixedArray* fixed_array,
4281 int array_length) {
4282 int position = 0;
4283 for (int i = 0; i < array_length; i++) {
4284 Object* element = fixed_array->get(i);
4285 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004286 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004287 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004288 int pos;
4289 int len;
4290 if (encoded_slice > 0) {
4291 // Position and length encoded in one smi.
4292 pos = StringBuilderSubstringPosition::decode(encoded_slice);
4293 len = StringBuilderSubstringLength::decode(encoded_slice);
4294 } else {
4295 // Position and length encoded in two smis.
4296 Object* obj = fixed_array->get(++i);
4297 ASSERT(obj->IsSmi());
4298 pos = Smi::cast(obj)->value();
4299 len = -encoded_slice;
4300 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00004301 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004302 sink + position,
4303 pos,
4304 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004305 position += len;
4306 } else {
4307 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004308 int element_length = string->length();
4309 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004310 position += element_length;
4311 }
4312 }
4313}
4314
4315
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004316static Object* Runtime_StringBuilderConcat(Arguments args) {
4317 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004318 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004319 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004320 if (!args[1]->IsSmi()) {
4321 Top::context()->mark_out_of_memory();
4322 return Failure::OutOfMemoryException();
4323 }
4324 int array_length = Smi::cast(args[1])->value();
4325 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004326
4327 // This assumption is used by the slice encoding in one or two smis.
4328 ASSERT(Smi::kMaxValue >= String::kMaxLength);
4329
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004330 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004331 if (!array->HasFastElements()) {
4332 return Top::Throw(Heap::illegal_argument_symbol());
4333 }
4334 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004335 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004336 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004337 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004338
4339 if (array_length == 0) {
4340 return Heap::empty_string();
4341 } else if (array_length == 1) {
4342 Object* first = fixed_array->get(0);
4343 if (first->IsString()) return first;
4344 }
4345
ager@chromium.org5ec48922009-05-05 07:25:34 +00004346 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004347 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004348 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004349 for (int i = 0; i < array_length; i++) {
4350 Object* elt = fixed_array->get(i);
4351 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004352 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004353 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004354 if (len > 0) {
4355 // Position and length encoded in one smi.
4356 int pos = len >> 11;
4357 len &= 0x7ff;
4358 if (pos + len > special_length) {
4359 return Top::Throw(Heap::illegal_argument_symbol());
4360 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004361 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004362 } else {
4363 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004364 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004365 // Get the position and check that it is also a smi.
4366 i++;
4367 if (i >= array_length) {
4368 return Top::Throw(Heap::illegal_argument_symbol());
4369 }
4370 Object* pos = fixed_array->get(i);
4371 if (!pos->IsSmi()) {
4372 return Top::Throw(Heap::illegal_argument_symbol());
4373 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004374 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004375 } else if (elt->IsString()) {
4376 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004377 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004378 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004379 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004380 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004381 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004382 } else {
4383 return Top::Throw(Heap::illegal_argument_symbol());
4384 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004385 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004386 Top::context()->mark_out_of_memory();
4387 return Failure::OutOfMemoryException();
4388 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004389 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004390 }
4391
4392 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004393 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004394
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004395 if (ascii) {
4396 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004397 if (object->IsFailure()) return object;
4398 SeqAsciiString* answer = SeqAsciiString::cast(object);
4399 StringBuilderConcatHelper(special,
4400 answer->GetChars(),
4401 fixed_array,
4402 array_length);
4403 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004404 } else {
4405 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004406 if (object->IsFailure()) return object;
4407 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
4408 StringBuilderConcatHelper(special,
4409 answer->GetChars(),
4410 fixed_array,
4411 array_length);
4412 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004413 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004414}
4415
4416
4417static Object* Runtime_NumberOr(Arguments args) {
4418 NoHandleAllocation ha;
4419 ASSERT(args.length() == 2);
4420
4421 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4422 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4423 return Heap::NumberFromInt32(x | y);
4424}
4425
4426
4427static Object* Runtime_NumberAnd(Arguments args) {
4428 NoHandleAllocation ha;
4429 ASSERT(args.length() == 2);
4430
4431 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4432 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4433 return Heap::NumberFromInt32(x & y);
4434}
4435
4436
4437static Object* Runtime_NumberXor(Arguments args) {
4438 NoHandleAllocation ha;
4439 ASSERT(args.length() == 2);
4440
4441 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4442 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4443 return Heap::NumberFromInt32(x ^ y);
4444}
4445
4446
4447static Object* Runtime_NumberNot(Arguments args) {
4448 NoHandleAllocation ha;
4449 ASSERT(args.length() == 1);
4450
4451 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4452 return Heap::NumberFromInt32(~x);
4453}
4454
4455
4456static Object* Runtime_NumberShl(Arguments args) {
4457 NoHandleAllocation ha;
4458 ASSERT(args.length() == 2);
4459
4460 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4461 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4462 return Heap::NumberFromInt32(x << (y & 0x1f));
4463}
4464
4465
4466static Object* Runtime_NumberShr(Arguments args) {
4467 NoHandleAllocation ha;
4468 ASSERT(args.length() == 2);
4469
4470 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
4471 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4472 return Heap::NumberFromUint32(x >> (y & 0x1f));
4473}
4474
4475
4476static Object* Runtime_NumberSar(Arguments args) {
4477 NoHandleAllocation ha;
4478 ASSERT(args.length() == 2);
4479
4480 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4481 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4482 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
4483}
4484
4485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004486static Object* Runtime_NumberEquals(Arguments args) {
4487 NoHandleAllocation ha;
4488 ASSERT(args.length() == 2);
4489
4490 CONVERT_DOUBLE_CHECKED(x, args[0]);
4491 CONVERT_DOUBLE_CHECKED(y, args[1]);
4492 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
4493 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
4494 if (x == y) return Smi::FromInt(EQUAL);
4495 Object* result;
4496 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
4497 result = Smi::FromInt(EQUAL);
4498 } else {
4499 result = Smi::FromInt(NOT_EQUAL);
4500 }
4501 return result;
4502}
4503
4504
4505static Object* Runtime_StringEquals(Arguments args) {
4506 NoHandleAllocation ha;
4507 ASSERT(args.length() == 2);
4508
4509 CONVERT_CHECKED(String, x, args[0]);
4510 CONVERT_CHECKED(String, y, args[1]);
4511
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004512 bool not_equal = !x->Equals(y);
4513 // This is slightly convoluted because the value that signifies
4514 // equality is 0 and inequality is 1 so we have to negate the result
4515 // from String::Equals.
4516 ASSERT(not_equal == 0 || not_equal == 1);
4517 STATIC_CHECK(EQUAL == 0);
4518 STATIC_CHECK(NOT_EQUAL == 1);
4519 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004520}
4521
4522
4523static Object* Runtime_NumberCompare(Arguments args) {
4524 NoHandleAllocation ha;
4525 ASSERT(args.length() == 3);
4526
4527 CONVERT_DOUBLE_CHECKED(x, args[0]);
4528 CONVERT_DOUBLE_CHECKED(y, args[1]);
4529 if (isnan(x) || isnan(y)) return args[2];
4530 if (x == y) return Smi::FromInt(EQUAL);
4531 if (isless(x, y)) return Smi::FromInt(LESS);
4532 return Smi::FromInt(GREATER);
4533}
4534
4535
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004536// Compare two Smis as if they were converted to strings and then
4537// compared lexicographically.
4538static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4539 NoHandleAllocation ha;
4540 ASSERT(args.length() == 2);
4541
4542 // Arrays for the individual characters of the two Smis. Smis are
4543 // 31 bit integers and 10 decimal digits are therefore enough.
4544 static int x_elms[10];
4545 static int y_elms[10];
4546
4547 // Extract the integer values from the Smis.
4548 CONVERT_CHECKED(Smi, x, args[0]);
4549 CONVERT_CHECKED(Smi, y, args[1]);
4550 int x_value = x->value();
4551 int y_value = y->value();
4552
4553 // If the integers are equal so are the string representations.
4554 if (x_value == y_value) return Smi::FromInt(EQUAL);
4555
4556 // If one of the integers are zero the normal integer order is the
4557 // same as the lexicographic order of the string representations.
4558 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4559
ager@chromium.org32912102009-01-16 10:38:43 +00004560 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004561 // smallest because the char code of '-' is less than the char code
4562 // of any digit. Otherwise, we make both values positive.
4563 if (x_value < 0 || y_value < 0) {
4564 if (y_value >= 0) return Smi::FromInt(LESS);
4565 if (x_value >= 0) return Smi::FromInt(GREATER);
4566 x_value = -x_value;
4567 y_value = -y_value;
4568 }
4569
4570 // Convert the integers to arrays of their decimal digits.
4571 int x_index = 0;
4572 int y_index = 0;
4573 while (x_value > 0) {
4574 x_elms[x_index++] = x_value % 10;
4575 x_value /= 10;
4576 }
4577 while (y_value > 0) {
4578 y_elms[y_index++] = y_value % 10;
4579 y_value /= 10;
4580 }
4581
4582 // Loop through the arrays of decimal digits finding the first place
4583 // where they differ.
4584 while (--x_index >= 0 && --y_index >= 0) {
4585 int diff = x_elms[x_index] - y_elms[y_index];
4586 if (diff != 0) return Smi::FromInt(diff);
4587 }
4588
4589 // If one array is a suffix of the other array, the longest array is
4590 // the representation of the largest of the Smis in the
4591 // lexicographic ordering.
4592 return Smi::FromInt(x_index - y_index);
4593}
4594
4595
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004596static Object* Runtime_StringCompare(Arguments args) {
4597 NoHandleAllocation ha;
4598 ASSERT(args.length() == 2);
4599
4600 CONVERT_CHECKED(String, x, args[0]);
4601 CONVERT_CHECKED(String, y, args[1]);
4602
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004603 Counters::string_compare_runtime.Increment();
4604
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004605 // A few fast case tests before we flatten.
4606 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004607 if (y->length() == 0) {
4608 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004609 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004610 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004611 return Smi::FromInt(LESS);
4612 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004613
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004614 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004615 if (d < 0) return Smi::FromInt(LESS);
4616 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004617
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004618 x->TryFlattenIfNotFlat();
4619 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004620
4621 static StringInputBuffer bufx;
4622 static StringInputBuffer bufy;
4623 bufx.Reset(x);
4624 bufy.Reset(y);
4625 while (bufx.has_more() && bufy.has_more()) {
4626 int d = bufx.GetNext() - bufy.GetNext();
4627 if (d < 0) return Smi::FromInt(LESS);
4628 else if (d > 0) return Smi::FromInt(GREATER);
4629 }
4630
4631 // x is (non-trivial) prefix of y:
4632 if (bufy.has_more()) return Smi::FromInt(LESS);
4633 // y is prefix of x:
4634 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4635}
4636
4637
4638static Object* Runtime_Math_abs(Arguments args) {
4639 NoHandleAllocation ha;
4640 ASSERT(args.length() == 1);
4641
4642 CONVERT_DOUBLE_CHECKED(x, args[0]);
4643 return Heap::AllocateHeapNumber(fabs(x));
4644}
4645
4646
4647static Object* Runtime_Math_acos(Arguments args) {
4648 NoHandleAllocation ha;
4649 ASSERT(args.length() == 1);
4650
4651 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004652 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004653}
4654
4655
4656static Object* Runtime_Math_asin(Arguments args) {
4657 NoHandleAllocation ha;
4658 ASSERT(args.length() == 1);
4659
4660 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004661 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004662}
4663
4664
4665static Object* Runtime_Math_atan(Arguments args) {
4666 NoHandleAllocation ha;
4667 ASSERT(args.length() == 1);
4668
4669 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004670 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004671}
4672
4673
4674static Object* Runtime_Math_atan2(Arguments args) {
4675 NoHandleAllocation ha;
4676 ASSERT(args.length() == 2);
4677
4678 CONVERT_DOUBLE_CHECKED(x, args[0]);
4679 CONVERT_DOUBLE_CHECKED(y, args[1]);
4680 double result;
4681 if (isinf(x) && isinf(y)) {
4682 // Make sure that the result in case of two infinite arguments
4683 // is a multiple of Pi / 4. The sign of the result is determined
4684 // by the first argument (x) and the sign of the second argument
4685 // determines the multiplier: one or three.
4686 static double kPiDividedBy4 = 0.78539816339744830962;
4687 int multiplier = (x < 0) ? -1 : 1;
4688 if (y < 0) multiplier *= 3;
4689 result = multiplier * kPiDividedBy4;
4690 } else {
4691 result = atan2(x, y);
4692 }
4693 return Heap::AllocateHeapNumber(result);
4694}
4695
4696
4697static Object* Runtime_Math_ceil(Arguments args) {
4698 NoHandleAllocation ha;
4699 ASSERT(args.length() == 1);
4700
4701 CONVERT_DOUBLE_CHECKED(x, args[0]);
4702 return Heap::NumberFromDouble(ceiling(x));
4703}
4704
4705
4706static Object* Runtime_Math_cos(Arguments args) {
4707 NoHandleAllocation ha;
4708 ASSERT(args.length() == 1);
4709
4710 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004711 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712}
4713
4714
4715static Object* Runtime_Math_exp(Arguments args) {
4716 NoHandleAllocation ha;
4717 ASSERT(args.length() == 1);
4718
4719 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004720 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004721}
4722
4723
4724static Object* Runtime_Math_floor(Arguments args) {
4725 NoHandleAllocation ha;
4726 ASSERT(args.length() == 1);
4727
4728 CONVERT_DOUBLE_CHECKED(x, args[0]);
4729 return Heap::NumberFromDouble(floor(x));
4730}
4731
4732
4733static Object* Runtime_Math_log(Arguments args) {
4734 NoHandleAllocation ha;
4735 ASSERT(args.length() == 1);
4736
4737 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004738 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004739}
4740
4741
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004742// Helper function to compute x^y, where y is known to be an
4743// integer. Uses binary decomposition to limit the number of
4744// multiplications; see the discussion in "Hacker's Delight" by Henry
4745// S. Warren, Jr., figure 11-6, page 213.
4746static double powi(double x, int y) {
4747 ASSERT(y != kMinInt);
4748 unsigned n = (y < 0) ? -y : y;
4749 double m = x;
4750 double p = 1;
4751 while (true) {
4752 if ((n & 1) != 0) p *= m;
4753 n >>= 1;
4754 if (n == 0) {
4755 if (y < 0) {
4756 // Unfortunately, we have to be careful when p has reached
4757 // infinity in the computation, because sometimes the higher
4758 // internal precision in the pow() implementation would have
4759 // given us a finite p. This happens very rarely.
4760 double result = 1.0 / p;
4761 return (result == 0 && isinf(p))
4762 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4763 : result;
4764 } else {
4765 return p;
4766 }
4767 }
4768 m *= m;
4769 }
4770}
4771
4772
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004773static Object* Runtime_Math_pow(Arguments args) {
4774 NoHandleAllocation ha;
4775 ASSERT(args.length() == 2);
4776
4777 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004778
4779 // If the second argument is a smi, it is much faster to call the
4780 // custom powi() function than the generic pow().
4781 if (args[1]->IsSmi()) {
4782 int y = Smi::cast(args[1])->value();
4783 return Heap::AllocateHeapNumber(powi(x, y));
4784 }
4785
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004786 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004787
4788 if (!isinf(x)) {
4789 if (y == 0.5) {
4790 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4791 // square root of a number. To speed up such computations, we
4792 // explictly check for this case and use the sqrt() function
4793 // which is faster than pow().
4794 return Heap::AllocateHeapNumber(sqrt(x));
4795 } else if (y == -0.5) {
4796 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4797 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4798 }
4799 }
4800
4801 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004803 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4804 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004805 } else {
4806 return Heap::AllocateHeapNumber(pow(x, y));
4807 }
4808}
4809
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004810
4811static Object* Runtime_Math_round(Arguments args) {
4812 NoHandleAllocation ha;
4813 ASSERT(args.length() == 1);
4814
4815 CONVERT_DOUBLE_CHECKED(x, args[0]);
4816 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004817 double integer = ceil(x);
4818 if (integer - x > 0.5) { integer -= 1.0; }
4819 return Heap::NumberFromDouble(integer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820}
4821
4822
4823static Object* Runtime_Math_sin(Arguments args) {
4824 NoHandleAllocation ha;
4825 ASSERT(args.length() == 1);
4826
4827 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004828 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829}
4830
4831
4832static Object* Runtime_Math_sqrt(Arguments args) {
4833 NoHandleAllocation ha;
4834 ASSERT(args.length() == 1);
4835
4836 CONVERT_DOUBLE_CHECKED(x, args[0]);
4837 return Heap::AllocateHeapNumber(sqrt(x));
4838}
4839
4840
4841static Object* Runtime_Math_tan(Arguments args) {
4842 NoHandleAllocation ha;
4843 ASSERT(args.length() == 1);
4844
4845 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004846 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004847}
4848
4849
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004850static Object* Runtime_NewArgumentsFast(Arguments args) {
4851 NoHandleAllocation ha;
4852 ASSERT(args.length() == 3);
4853
4854 JSFunction* callee = JSFunction::cast(args[0]);
4855 Object** parameters = reinterpret_cast<Object**>(args[1]);
4856 const int length = Smi::cast(args[2])->value();
4857
4858 Object* result = Heap::AllocateArgumentsObject(callee, length);
4859 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004860 // Allocate the elements if needed.
4861 if (length > 0) {
4862 // Allocate the fixed array.
4863 Object* obj = Heap::AllocateRawFixedArray(length);
4864 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004865
4866 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004867 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4868 FixedArray* array = FixedArray::cast(obj);
4869 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004870
4871 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004872 for (int i = 0; i < length; i++) {
4873 array->set(i, *--parameters, mode);
4874 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004875 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004876 }
4877 return result;
4878}
4879
4880
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004881static Object* Runtime_NewClosure(Arguments args) {
4882 HandleScope scope;
4883 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00004884 CONVERT_ARG_CHECKED(Context, context, 0);
4885 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004886
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004887 PretenureFlag pretenure = (context->global_context() == *context)
4888 ? TENURED // Allocate global closures in old space.
4889 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004890 Handle<JSFunction> result =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004891 Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892 return *result;
4893}
4894
4895
ager@chromium.org5c838252010-02-19 08:53:10 +00004896static Code* ComputeConstructStub(Handle<JSFunction> function) {
4897 Handle<Object> prototype = Factory::null_value();
4898 if (function->has_instance_prototype()) {
4899 prototype = Handle<Object>(function->instance_prototype());
4900 }
4901 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004902 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00004903 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004904 if (code->IsFailure()) {
4905 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4906 }
4907 return Code::cast(code);
4908 }
4909
ager@chromium.org5c838252010-02-19 08:53:10 +00004910 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004911}
4912
4913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004914static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004915 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004916 ASSERT(args.length() == 1);
4917
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004918 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004919
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004920 // If the constructor isn't a proper function we throw a type error.
4921 if (!constructor->IsJSFunction()) {
4922 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4923 Handle<Object> type_error =
4924 Factory::NewTypeError("not_constructor", arguments);
4925 return Top::Throw(*type_error);
4926 }
4927
4928 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004929#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004930 // Handle stepping into constructors if step into is active.
4931 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004932 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004933 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004934#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004935
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004936 if (function->has_initial_map()) {
4937 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004938 // The 'Function' function ignores the receiver object when
4939 // called using 'new' and creates a new JSFunction object that
4940 // is returned. The receiver object is only used for error
4941 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004942 // JSFunction. Factory::NewJSObject() should not be used to
4943 // allocate JSFunctions since it does not properly initialize
4944 // the shared part of the function. Since the receiver is
4945 // ignored anyway, we use the global object as the receiver
4946 // instead of a new JSFunction object. This way, errors are
4947 // reported the same way whether or not 'Function' is called
4948 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004949 return Top::context()->global();
4950 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004951 }
4952
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004953 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004954 Handle<SharedFunctionInfo> shared(function->shared());
4955 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004956
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004957 bool first_allocation = !function->has_initial_map();
4958 Handle<JSObject> result = Factory::NewJSObject(function);
4959 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004960 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00004961 ComputeConstructStub(Handle<JSFunction>(function)));
4962 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004963 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004964
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004965 Counters::constructed_objects.Increment();
4966 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004967
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004968 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004969}
4970
4971
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004972static Object* Runtime_LazyCompile(Arguments args) {
4973 HandleScope scope;
4974 ASSERT(args.length() == 1);
4975
4976 Handle<JSFunction> function = args.at<JSFunction>(0);
4977#ifdef DEBUG
4978 if (FLAG_trace_lazy) {
4979 PrintF("[lazy: ");
4980 function->shared()->name()->Print();
4981 PrintF("]\n");
4982 }
4983#endif
4984
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004985 // Compile the target function. Here we compile using CompileLazyInLoop in
4986 // order to get the optimized version. This helps code like delta-blue
4987 // that calls performance-critical routines through constructors. A
4988 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4989 // direct call. Since the in-loop tracking takes place through CallICs
4990 // this means that things called through constructors are never known to
4991 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004992 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004993 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004994 return Failure::Exception();
4995 }
4996
4997 return function->code();
4998}
4999
5000
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005001static Object* Runtime_GetFunctionDelegate(Arguments args) {
5002 HandleScope scope;
5003 ASSERT(args.length() == 1);
5004 RUNTIME_ASSERT(!args[0]->IsJSFunction());
5005 return *Execution::GetFunctionDelegate(args.at<Object>(0));
5006}
5007
5008
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00005009static Object* Runtime_GetConstructorDelegate(Arguments args) {
5010 HandleScope scope;
5011 ASSERT(args.length() == 1);
5012 RUNTIME_ASSERT(!args[0]->IsJSFunction());
5013 return *Execution::GetConstructorDelegate(args.at<Object>(0));
5014}
5015
5016
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005017static Object* Runtime_NewContext(Arguments args) {
5018 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00005019 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005020
kasper.lund7276f142008-07-30 08:49:36 +00005021 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005022 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
5023 Object* result = Heap::AllocateFunctionContext(length, function);
5024 if (result->IsFailure()) return result;
5025
5026 Top::set_context(Context::cast(result));
5027
kasper.lund7276f142008-07-30 08:49:36 +00005028 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005029}
5030
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005031static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005032 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005033 Object* js_object = object;
5034 if (!js_object->IsJSObject()) {
5035 js_object = js_object->ToObject();
5036 if (js_object->IsFailure()) {
5037 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005038 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005039 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005040 Handle<Object> result =
5041 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
5042 return Top::Throw(*result);
5043 }
5044 }
5045
5046 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005047 Heap::AllocateWithContext(Top::context(),
5048 JSObject::cast(js_object),
5049 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005050 if (result->IsFailure()) return result;
5051
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005052 Context* context = Context::cast(result);
5053 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005054
kasper.lund7276f142008-07-30 08:49:36 +00005055 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005056}
5057
5058
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005059static Object* Runtime_PushContext(Arguments args) {
5060 NoHandleAllocation ha;
5061 ASSERT(args.length() == 1);
5062 return PushContextHelper(args[0], false);
5063}
5064
5065
5066static Object* Runtime_PushCatchContext(Arguments args) {
5067 NoHandleAllocation ha;
5068 ASSERT(args.length() == 1);
5069 return PushContextHelper(args[0], true);
5070}
5071
5072
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005073static Object* Runtime_LookupContext(Arguments args) {
5074 HandleScope scope;
5075 ASSERT(args.length() == 2);
5076
5077 CONVERT_ARG_CHECKED(Context, context, 0);
5078 CONVERT_ARG_CHECKED(String, name, 1);
5079
5080 int index;
5081 PropertyAttributes attributes;
5082 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005083 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005084 context->Lookup(name, flags, &index, &attributes);
5085
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005086 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005087 ASSERT(holder->IsJSObject());
5088 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005089 }
5090
5091 // No intermediate context found. Use global object by default.
5092 return Top::context()->global();
5093}
5094
5095
ager@chromium.orga1645e22009-09-09 19:27:10 +00005096// A mechanism to return a pair of Object pointers in registers (if possible).
5097// How this is achieved is calling convention-dependent.
5098// All currently supported x86 compiles uses calling conventions that are cdecl
5099// variants where a 64-bit value is returned in two 32-bit registers
5100// (edx:eax on ia32, r1:r0 on ARM).
5101// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
5102// In Win64 calling convention, a struct of two pointers is returned in memory,
5103// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005104#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005105struct ObjectPair {
5106 Object* x;
5107 Object* y;
5108};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005109
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005110static inline ObjectPair MakePair(Object* x, Object* y) {
5111 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005112 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
5113 // In Win64 they are assigned to a hidden first argument.
5114 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005115}
5116#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005117typedef uint64_t ObjectPair;
5118static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005119 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005120 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005121}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005122#endif
5123
5124
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005125static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005126 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
5127 USE(attributes);
5128 return x->IsTheHole() ? Heap::undefined_value() : x;
5129}
5130
5131
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005132static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
5133 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005134 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005135 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005136 JSFunction* context_extension_function =
5137 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005138 // If the holder isn't a context extension object, we just return it
5139 // as the receiver. This allows arguments objects to be used as
5140 // receivers, but only if they are put in the context scope chain
5141 // explicitly via a with-statement.
5142 Object* constructor = holder->map()->constructor();
5143 if (constructor != context_extension_function) return holder;
5144 // Fall back to using the global object as the receiver if the
5145 // property turns out to be a local variable allocated in a context
5146 // extension object - introduced via eval.
5147 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005148}
5149
5150
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005151static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005152 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00005153 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005154
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005155 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005156 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005157 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005158 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005159 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005160
5161 int index;
5162 PropertyAttributes attributes;
5163 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005164 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005165 context->Lookup(name, flags, &index, &attributes);
5166
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005167 // If the index is non-negative, the slot has been found in a local
5168 // variable or a parameter. Read it from the context object or the
5169 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005170 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005171 // If the "property" we were looking for is a local variable or an
5172 // argument in a context, the receiver is the global object; see
5173 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
5174 JSObject* receiver = Top::context()->global()->global_receiver();
5175 Object* value = (holder->IsContext())
5176 ? Context::cast(*holder)->get(index)
5177 : JSObject::cast(*holder)->GetElement(index);
5178 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005179 }
5180
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005181 // If the holder is found, we read the property from it.
5182 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005183 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005184 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005185 JSObject* receiver;
5186 if (object->IsGlobalObject()) {
5187 receiver = GlobalObject::cast(object)->global_receiver();
5188 } else if (context->is_exception_holder(*holder)) {
5189 receiver = Top::context()->global()->global_receiver();
5190 } else {
5191 receiver = ComputeReceiverForNonGlobal(object);
5192 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005193 // No need to unhole the value here. This is taken care of by the
5194 // GetProperty function.
5195 Object* value = object->GetProperty(*name);
5196 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005197 }
5198
5199 if (throw_error) {
5200 // The property doesn't exist - throw exception.
5201 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005202 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005203 return MakePair(Top::Throw(*reference_error), NULL);
5204 } else {
5205 // The property doesn't exist - return undefined
5206 return MakePair(Heap::undefined_value(), Heap::undefined_value());
5207 }
5208}
5209
5210
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005211static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005212 return LoadContextSlotHelper(args, true);
5213}
5214
5215
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005216static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005217 return LoadContextSlotHelper(args, false);
5218}
5219
5220
5221static Object* Runtime_StoreContextSlot(Arguments args) {
5222 HandleScope scope;
5223 ASSERT(args.length() == 3);
5224
5225 Handle<Object> value(args[0]);
5226 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005227 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005228
5229 int index;
5230 PropertyAttributes attributes;
5231 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005232 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005233 context->Lookup(name, flags, &index, &attributes);
5234
5235 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005236 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005237 // Ignore if read_only variable.
5238 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005239 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005240 }
5241 } else {
5242 ASSERT((attributes & READ_ONLY) == 0);
5243 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005244 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005245 USE(result);
5246 ASSERT(!result->IsFailure());
5247 }
5248 return *value;
5249 }
5250
5251 // Slow case: The property is not in a FixedArray context.
5252 // It is either in an JSObject extension context or it was not found.
5253 Handle<JSObject> context_ext;
5254
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005255 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005256 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005257 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005258 } else {
5259 // The property was not found. It needs to be stored in the global context.
5260 ASSERT(attributes == ABSENT);
5261 attributes = NONE;
5262 context_ext = Handle<JSObject>(Top::context()->global());
5263 }
5264
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005265 // Set the property, but ignore if read_only variable on the context
5266 // extension object itself.
5267 if ((attributes & READ_ONLY) == 0 ||
5268 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005269 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
5270 if (set.is_null()) {
5271 // Failure::Exception is converted to a null handle in the
5272 // handle-based methods such as SetProperty. We therefore need
5273 // to convert null handles back to exceptions.
5274 ASSERT(Top::has_pending_exception());
5275 return Failure::Exception();
5276 }
5277 }
5278 return *value;
5279}
5280
5281
5282static Object* Runtime_Throw(Arguments args) {
5283 HandleScope scope;
5284 ASSERT(args.length() == 1);
5285
5286 return Top::Throw(args[0]);
5287}
5288
5289
5290static Object* Runtime_ReThrow(Arguments args) {
5291 HandleScope scope;
5292 ASSERT(args.length() == 1);
5293
5294 return Top::ReThrow(args[0]);
5295}
5296
5297
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005298static Object* Runtime_PromoteScheduledException(Arguments args) {
5299 ASSERT_EQ(0, args.length());
5300 return Top::PromoteScheduledException();
5301}
5302
5303
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005304static Object* Runtime_ThrowReferenceError(Arguments args) {
5305 HandleScope scope;
5306 ASSERT(args.length() == 1);
5307
5308 Handle<Object> name(args[0]);
5309 Handle<Object> reference_error =
5310 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5311 return Top::Throw(*reference_error);
5312}
5313
5314
5315static Object* Runtime_StackOverflow(Arguments args) {
5316 NoHandleAllocation na;
5317 return Top::StackOverflow();
5318}
5319
5320
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005321static Object* Runtime_StackGuard(Arguments args) {
5322 ASSERT(args.length() == 1);
5323
5324 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005325 if (StackGuard::IsStackOverflow()) {
5326 return Runtime_StackOverflow(args);
5327 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005328
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005329 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005330}
5331
5332
5333// NOTE: These PrintXXX functions are defined for all builds (not just
5334// DEBUG builds) because we may want to be able to trace function
5335// calls in all modes.
5336static void PrintString(String* str) {
5337 // not uncommon to have empty strings
5338 if (str->length() > 0) {
5339 SmartPointer<char> s =
5340 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
5341 PrintF("%s", *s);
5342 }
5343}
5344
5345
5346static void PrintObject(Object* obj) {
5347 if (obj->IsSmi()) {
5348 PrintF("%d", Smi::cast(obj)->value());
5349 } else if (obj->IsString() || obj->IsSymbol()) {
5350 PrintString(String::cast(obj));
5351 } else if (obj->IsNumber()) {
5352 PrintF("%g", obj->Number());
5353 } else if (obj->IsFailure()) {
5354 PrintF("<failure>");
5355 } else if (obj->IsUndefined()) {
5356 PrintF("<undefined>");
5357 } else if (obj->IsNull()) {
5358 PrintF("<null>");
5359 } else if (obj->IsTrue()) {
5360 PrintF("<true>");
5361 } else if (obj->IsFalse()) {
5362 PrintF("<false>");
5363 } else {
5364 PrintF("%p", obj);
5365 }
5366}
5367
5368
5369static int StackSize() {
5370 int n = 0;
5371 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
5372 return n;
5373}
5374
5375
5376static void PrintTransition(Object* result) {
5377 // indentation
5378 { const int nmax = 80;
5379 int n = StackSize();
5380 if (n <= nmax)
5381 PrintF("%4d:%*s", n, n, "");
5382 else
5383 PrintF("%4d:%*s", n, nmax, "...");
5384 }
5385
5386 if (result == NULL) {
5387 // constructor calls
5388 JavaScriptFrameIterator it;
5389 JavaScriptFrame* frame = it.frame();
5390 if (frame->IsConstructor()) PrintF("new ");
5391 // function name
5392 Object* fun = frame->function();
5393 if (fun->IsJSFunction()) {
5394 PrintObject(JSFunction::cast(fun)->shared()->name());
5395 } else {
5396 PrintObject(fun);
5397 }
5398 // function arguments
5399 // (we are intentionally only printing the actually
5400 // supplied parameters, not all parameters required)
5401 PrintF("(this=");
5402 PrintObject(frame->receiver());
5403 const int length = frame->GetProvidedParametersCount();
5404 for (int i = 0; i < length; i++) {
5405 PrintF(", ");
5406 PrintObject(frame->GetParameter(i));
5407 }
5408 PrintF(") {\n");
5409
5410 } else {
5411 // function result
5412 PrintF("} -> ");
5413 PrintObject(result);
5414 PrintF("\n");
5415 }
5416}
5417
5418
5419static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005420 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421 NoHandleAllocation ha;
5422 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005423 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005424}
5425
5426
5427static Object* Runtime_TraceExit(Arguments args) {
5428 NoHandleAllocation ha;
5429 PrintTransition(args[0]);
5430 return args[0]; // return TOS
5431}
5432
5433
5434static Object* Runtime_DebugPrint(Arguments args) {
5435 NoHandleAllocation ha;
5436 ASSERT(args.length() == 1);
5437
5438#ifdef DEBUG
5439 if (args[0]->IsString()) {
5440 // If we have a string, assume it's a code "marker"
5441 // and print some interesting cpu debugging info.
5442 JavaScriptFrameIterator it;
5443 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005444 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
5445 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005446 } else {
5447 PrintF("DebugPrint: ");
5448 }
5449 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005450 if (args[0]->IsHeapObject()) {
5451 HeapObject::cast(args[0])->map()->Print();
5452 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005453#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005454 // ShortPrint is available in release mode. Print is not.
5455 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005456#endif
5457 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00005458 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005459
5460 return args[0]; // return TOS
5461}
5462
5463
5464static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005465 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005466 NoHandleAllocation ha;
5467 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005468 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005469}
5470
5471
mads.s.ager31e71382008-08-13 09:32:07 +00005472static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005473 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005474 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005475
5476 // According to ECMA-262, section 15.9.1, page 117, the precision of
5477 // the number in a Date object representing a particular instant in
5478 // time is milliseconds. Therefore, we floor the result of getting
5479 // the OS time.
5480 double millis = floor(OS::TimeCurrentMillis());
5481 return Heap::NumberFromDouble(millis);
5482}
5483
5484
5485static Object* Runtime_DateParseString(Arguments args) {
5486 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005487 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005488
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005489 CONVERT_ARG_CHECKED(String, str, 0);
5490 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005491
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005492 CONVERT_ARG_CHECKED(JSArray, output, 1);
5493 RUNTIME_ASSERT(output->HasFastElements());
5494
5495 AssertNoAllocation no_allocation;
5496
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005497 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005498 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5499 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005500 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005501 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005503 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005504 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5505 }
5506
5507 if (result) {
5508 return *output;
5509 } else {
5510 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511 }
5512}
5513
5514
5515static Object* Runtime_DateLocalTimezone(Arguments args) {
5516 NoHandleAllocation ha;
5517 ASSERT(args.length() == 1);
5518
5519 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005520 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005521 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5522}
5523
5524
5525static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5526 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005527 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005528
5529 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5530}
5531
5532
5533static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5534 NoHandleAllocation ha;
5535 ASSERT(args.length() == 1);
5536
5537 CONVERT_DOUBLE_CHECKED(x, args[0]);
5538 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5539}
5540
5541
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542static Object* Runtime_NumberIsFinite(Arguments args) {
5543 NoHandleAllocation ha;
5544 ASSERT(args.length() == 1);
5545
5546 CONVERT_DOUBLE_CHECKED(value, args[0]);
5547 Object* result;
5548 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5549 result = Heap::false_value();
5550 } else {
5551 result = Heap::true_value();
5552 }
5553 return result;
5554}
5555
5556
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005557static Object* Runtime_GlobalReceiver(Arguments args) {
5558 ASSERT(args.length() == 1);
5559 Object* global = args[0];
5560 if (!global->IsJSGlobalObject()) return Heap::null_value();
5561 return JSGlobalObject::cast(global)->global_receiver();
5562}
5563
5564
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005565static Object* Runtime_CompileString(Arguments args) {
5566 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005567 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005568 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005569 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005570
ager@chromium.org381abbb2009-02-25 13:23:22 +00005571 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005572 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005573 Compiler::ValidationState validate = (is_json->IsTrue())
5574 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005575 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5576 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005577 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005578 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005579 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005580 Handle<JSFunction> fun =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005581 Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005582 return *fun;
5583}
5584
5585
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005586static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
5587 ASSERT(args.length() == 3);
5588 if (!args[0]->IsJSFunction()) {
5589 return MakePair(Top::ThrowIllegalOperation(), NULL);
5590 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005591
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005592 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005593 Handle<JSFunction> callee = args.at<JSFunction>(0);
5594 Handle<Object> receiver; // Will be overwritten.
5595
5596 // Compute the calling context.
5597 Handle<Context> context = Handle<Context>(Top::context());
5598#ifdef DEBUG
5599 // Make sure Top::context() agrees with the old code that traversed
5600 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005601 StackFrameLocator locator;
5602 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005603 ASSERT(Context::cast(frame->context()) == *context);
5604#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005605
5606 // Find where the 'eval' symbol is bound. It is unaliased only if
5607 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005608 int index = -1;
5609 PropertyAttributes attributes = ABSENT;
5610 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005611 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5612 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005613 // Stop search when eval is found or when the global context is
5614 // reached.
5615 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005616 if (context->is_function_context()) {
5617 context = Handle<Context>(Context::cast(context->closure()->context()));
5618 } else {
5619 context = Handle<Context>(context->previous());
5620 }
5621 }
5622
iposva@chromium.org245aa852009-02-10 00:49:54 +00005623 // If eval could not be resolved, it has been deleted and we need to
5624 // throw a reference error.
5625 if (attributes == ABSENT) {
5626 Handle<Object> name = Factory::eval_symbol();
5627 Handle<Object> reference_error =
5628 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005629 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005630 }
5631
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005632 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005633 // 'eval' is not bound in the global context. Just call the function
5634 // with the given arguments. This is not necessarily the global eval.
5635 if (receiver->IsContext()) {
5636 context = Handle<Context>::cast(receiver);
5637 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005638 } else if (receiver->IsJSContextExtensionObject()) {
5639 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005640 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005641 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005642 }
5643
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005644 // 'eval' is bound in the global context, but it may have been overwritten.
5645 // Compare it to the builtin 'GlobalEval' function to make sure.
5646 if (*callee != Top::global_context()->global_eval_fun() ||
5647 !args[1]->IsString()) {
5648 return MakePair(*callee, Top::context()->global()->global_receiver());
5649 }
5650
5651 // Deal with a normal eval call with a string argument. Compile it
5652 // and return the compiled function bound in the local context.
5653 Handle<String> source = args.at<String>(1);
5654 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5655 source,
5656 Handle<Context>(Top::context()),
5657 Top::context()->IsGlobalContext(),
5658 Compiler::DONT_VALIDATE_JSON);
5659 if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
5660 callee = Factory::NewFunctionFromBoilerplate(
5661 boilerplate,
5662 Handle<Context>(Top::context()),
5663 NOT_TENURED);
5664 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005665}
5666
5667
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005668static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5669 // This utility adjusts the property attributes for newly created Function
5670 // object ("new Function(...)") by changing the map.
5671 // All it does is changing the prototype property to enumerable
5672 // as specified in ECMA262, 15.3.5.2.
5673 HandleScope scope;
5674 ASSERT(args.length() == 1);
5675 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5676 ASSERT(func->map()->instance_type() ==
5677 Top::function_instance_map()->instance_type());
5678 ASSERT(func->map()->instance_size() ==
5679 Top::function_instance_map()->instance_size());
5680 func->set_map(*Top::function_instance_map());
5681 return *func;
5682}
5683
5684
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005685// Push an array unto an array of arrays if it is not already in the
5686// array. Returns true if the element was pushed on the stack and
5687// false otherwise.
5688static Object* Runtime_PushIfAbsent(Arguments args) {
5689 ASSERT(args.length() == 2);
5690 CONVERT_CHECKED(JSArray, array, args[0]);
5691 CONVERT_CHECKED(JSArray, element, args[1]);
5692 RUNTIME_ASSERT(array->HasFastElements());
5693 int length = Smi::cast(array->length())->value();
5694 FixedArray* elements = FixedArray::cast(array->elements());
5695 for (int i = 0; i < length; i++) {
5696 if (elements->get(i) == element) return Heap::false_value();
5697 }
5698 Object* obj = array->SetFastElement(length, element);
5699 if (obj->IsFailure()) return obj;
5700 return Heap::true_value();
5701}
5702
5703
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005704/**
5705 * A simple visitor visits every element of Array's.
5706 * The backend storage can be a fixed array for fast elements case,
5707 * or a dictionary for sparse array. Since Dictionary is a subtype
5708 * of FixedArray, the class can be used by both fast and slow cases.
5709 * The second parameter of the constructor, fast_elements, specifies
5710 * whether the storage is a FixedArray or Dictionary.
5711 *
5712 * An index limit is used to deal with the situation that a result array
5713 * length overflows 32-bit non-negative integer.
5714 */
5715class ArrayConcatVisitor {
5716 public:
5717 ArrayConcatVisitor(Handle<FixedArray> storage,
5718 uint32_t index_limit,
5719 bool fast_elements) :
5720 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005721 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005722
5723 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005724 if (i >= index_limit_ - index_offset_) return;
5725 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005726
5727 if (fast_elements_) {
5728 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5729 storage_->set(index, *elm);
5730
5731 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005732 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5733 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005734 Factory::DictionaryAtNumberPut(dict, index, elm);
5735 if (!result.is_identical_to(dict))
5736 storage_ = result;
5737 }
5738 }
5739
5740 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005741 if (index_limit_ - index_offset_ < delta) {
5742 index_offset_ = index_limit_;
5743 } else {
5744 index_offset_ += delta;
5745 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005746 }
5747
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00005748 Handle<FixedArray> storage() { return storage_; }
5749
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005750 private:
5751 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005752 // Limit on the accepted indices. Elements with indices larger than the
5753 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005754 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005755 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005756 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005757 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005758};
5759
5760
ager@chromium.org3811b432009-10-28 14:53:37 +00005761template<class ExternalArrayClass, class ElementType>
5762static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5763 bool elements_are_ints,
5764 bool elements_are_guaranteed_smis,
5765 uint32_t range,
5766 ArrayConcatVisitor* visitor) {
5767 Handle<ExternalArrayClass> array(
5768 ExternalArrayClass::cast(receiver->elements()));
5769 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5770
5771 if (visitor != NULL) {
5772 if (elements_are_ints) {
5773 if (elements_are_guaranteed_smis) {
5774 for (uint32_t j = 0; j < len; j++) {
5775 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5776 visitor->visit(j, e);
5777 }
5778 } else {
5779 for (uint32_t j = 0; j < len; j++) {
5780 int64_t val = static_cast<int64_t>(array->get(j));
5781 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5782 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5783 visitor->visit(j, e);
5784 } else {
5785 Handle<Object> e(
5786 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5787 visitor->visit(j, e);
5788 }
5789 }
5790 }
5791 } else {
5792 for (uint32_t j = 0; j < len; j++) {
5793 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5794 visitor->visit(j, e);
5795 }
5796 }
5797 }
5798
5799 return len;
5800}
5801
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005802/**
5803 * A helper function that visits elements of a JSObject. Only elements
5804 * whose index between 0 and range (exclusive) are visited.
5805 *
5806 * If the third parameter, visitor, is not NULL, the visitor is called
5807 * with parameters, 'visitor_index_offset + element index' and the element.
5808 *
5809 * It returns the number of visisted elements.
5810 */
5811static uint32_t IterateElements(Handle<JSObject> receiver,
5812 uint32_t range,
5813 ArrayConcatVisitor* visitor) {
5814 uint32_t num_of_elements = 0;
5815
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005816 switch (receiver->GetElementsKind()) {
5817 case JSObject::FAST_ELEMENTS: {
5818 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5819 uint32_t len = elements->length();
5820 if (range < len) {
5821 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005822 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005823
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005824 for (uint32_t j = 0; j < len; j++) {
5825 Handle<Object> e(elements->get(j));
5826 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005827 num_of_elements++;
5828 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005829 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005830 }
5831 }
5832 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005833 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005834 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005835 case JSObject::PIXEL_ELEMENTS: {
5836 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5837 uint32_t len = pixels->length();
5838 if (range < len) {
5839 len = range;
5840 }
5841
5842 for (uint32_t j = 0; j < len; j++) {
5843 num_of_elements++;
5844 if (visitor != NULL) {
5845 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5846 visitor->visit(j, e);
5847 }
5848 }
5849 break;
5850 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005851 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5852 num_of_elements =
5853 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5854 receiver, true, true, range, visitor);
5855 break;
5856 }
5857 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5858 num_of_elements =
5859 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5860 receiver, true, true, range, visitor);
5861 break;
5862 }
5863 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5864 num_of_elements =
5865 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5866 receiver, true, true, range, visitor);
5867 break;
5868 }
5869 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5870 num_of_elements =
5871 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5872 receiver, true, true, range, visitor);
5873 break;
5874 }
5875 case JSObject::EXTERNAL_INT_ELEMENTS: {
5876 num_of_elements =
5877 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5878 receiver, true, false, range, visitor);
5879 break;
5880 }
5881 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5882 num_of_elements =
5883 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5884 receiver, true, false, range, visitor);
5885 break;
5886 }
5887 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5888 num_of_elements =
5889 IterateExternalArrayElements<ExternalFloatArray, float>(
5890 receiver, false, false, range, visitor);
5891 break;
5892 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005893 case JSObject::DICTIONARY_ELEMENTS: {
5894 Handle<NumberDictionary> dict(receiver->element_dictionary());
5895 uint32_t capacity = dict->Capacity();
5896 for (uint32_t j = 0; j < capacity; j++) {
5897 Handle<Object> k(dict->KeyAt(j));
5898 if (dict->IsKey(*k)) {
5899 ASSERT(k->IsNumber());
5900 uint32_t index = static_cast<uint32_t>(k->Number());
5901 if (index < range) {
5902 num_of_elements++;
5903 if (visitor) {
5904 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5905 }
5906 }
5907 }
5908 }
5909 break;
5910 }
5911 default:
5912 UNREACHABLE();
5913 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005914 }
5915
5916 return num_of_elements;
5917}
5918
5919
5920/**
5921 * A helper function that visits elements of an Array object, and elements
5922 * on its prototypes.
5923 *
5924 * Elements on prototypes are visited first, and only elements whose indices
5925 * less than Array length are visited.
5926 *
5927 * If a ArrayConcatVisitor object is given, the visitor is called with
5928 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005929 *
5930 * The returned number of elements is an upper bound on the actual number
5931 * of elements added. If the same element occurs in more than one object
5932 * in the array's prototype chain, it will be counted more than once, but
5933 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005934 */
5935static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5936 ArrayConcatVisitor* visitor) {
5937 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5938 Handle<Object> obj = array;
5939
5940 static const int kEstimatedPrototypes = 3;
5941 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5942
5943 // Visit prototype first. If an element on the prototype is shadowed by
5944 // the inheritor using the same index, the ArrayConcatVisitor visits
5945 // the prototype element before the shadowing element.
5946 // The visitor can simply overwrite the old value by new value using
5947 // the same index. This follows Array::concat semantics.
5948 while (!obj->IsNull()) {
5949 objects.Add(Handle<JSObject>::cast(obj));
5950 obj = Handle<Object>(obj->GetPrototype());
5951 }
5952
5953 uint32_t nof_elements = 0;
5954 for (int i = objects.length() - 1; i >= 0; i--) {
5955 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005956 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005957 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005958
5959 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
5960 nof_elements = JSObject::kMaxElementCount;
5961 } else {
5962 nof_elements += encountered_elements;
5963 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005964 }
5965
5966 return nof_elements;
5967}
5968
5969
5970/**
5971 * A helper function of Runtime_ArrayConcat.
5972 *
5973 * The first argument is an Array of arrays and objects. It is the
5974 * same as the arguments array of Array::concat JS function.
5975 *
5976 * If an argument is an Array object, the function visits array
5977 * elements. If an argument is not an Array object, the function
5978 * visits the object as if it is an one-element array.
5979 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005980 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005981 * non-negative number is used as new length. For example, if one
5982 * array length is 2^32 - 1, second array length is 1, the
5983 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005984 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
5985 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005986 */
5987static uint32_t IterateArguments(Handle<JSArray> arguments,
5988 ArrayConcatVisitor* visitor) {
5989 uint32_t visited_elements = 0;
5990 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5991
5992 for (uint32_t i = 0; i < num_of_args; i++) {
5993 Handle<Object> obj(arguments->GetElement(i));
5994 if (obj->IsJSArray()) {
5995 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5996 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5997 uint32_t nof_elements =
5998 IterateArrayAndPrototypeElements(array, visitor);
5999 // Total elements of array and its prototype chain can be more than
6000 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006001 // the array length number of elements. We use the length as an estimate
6002 // for the actual number of elements added.
6003 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
6004 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
6005 visited_elements = JSArray::kMaxElementCount;
6006 } else {
6007 visited_elements += added_elements;
6008 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006009 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006010 } else {
6011 if (visitor) {
6012 visitor->visit(0, obj);
6013 visitor->increase_index_offset(1);
6014 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006015 if (visited_elements < JSArray::kMaxElementCount) {
6016 visited_elements++;
6017 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006018 }
6019 }
6020 return visited_elements;
6021}
6022
6023
6024/**
6025 * Array::concat implementation.
6026 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006027 * TODO(lrn): Fix non-compliance for very large concatenations and update to
6028 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006029 */
6030static Object* Runtime_ArrayConcat(Arguments args) {
6031 ASSERT(args.length() == 1);
6032 HandleScope handle_scope;
6033
6034 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
6035 Handle<JSArray> arguments(arg_arrays);
6036
6037 // Pass 1: estimate the number of elements of the result
6038 // (it could be more than real numbers if prototype has elements).
6039 uint32_t result_length = 0;
6040 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
6041
6042 { AssertNoAllocation nogc;
6043 for (uint32_t i = 0; i < num_of_args; i++) {
6044 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006045 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006046 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006047 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006048 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
6049 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006050 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006051 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006052 if (JSObject::kMaxElementCount - result_length < length_estimate) {
6053 result_length = JSObject::kMaxElementCount;
6054 break;
6055 }
6056 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006057 }
6058 }
6059
6060 // Allocate an empty array, will set length and content later.
6061 Handle<JSArray> result = Factory::NewJSArray(0);
6062
6063 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
6064 // If estimated number of elements is more than half of length, a
6065 // fixed array (fast case) is more time and space-efficient than a
6066 // dictionary.
6067 bool fast_case = (estimate_nof_elements * 2) >= result_length;
6068
6069 Handle<FixedArray> storage;
6070 if (fast_case) {
6071 // The backing storage array must have non-existing elements to
6072 // preserve holes across concat operations.
6073 storage = Factory::NewFixedArrayWithHoles(result_length);
6074
6075 } else {
6076 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
6077 uint32_t at_least_space_for = estimate_nof_elements +
6078 (estimate_nof_elements >> 2);
6079 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006080 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006081 }
6082
6083 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
6084
6085 ArrayConcatVisitor visitor(storage, result_length, fast_case);
6086
6087 IterateArguments(arguments, &visitor);
6088
6089 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00006090 // Please note the storage might have changed in the visitor.
6091 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006092
6093 return *result;
6094}
6095
6096
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006097// This will not allocate (flatten the string), but it may run
6098// very slowly for very deeply nested ConsStrings. For debugging use only.
6099static Object* Runtime_GlobalPrint(Arguments args) {
6100 NoHandleAllocation ha;
6101 ASSERT(args.length() == 1);
6102
6103 CONVERT_CHECKED(String, string, args[0]);
6104 StringInputBuffer buffer(string);
6105 while (buffer.has_more()) {
6106 uint16_t character = buffer.GetNext();
6107 PrintF("%c", character);
6108 }
6109 return string;
6110}
6111
ager@chromium.org5ec48922009-05-05 07:25:34 +00006112// Moves all own elements of an object, that are below a limit, to positions
6113// starting at zero. All undefined values are placed after non-undefined values,
6114// and are followed by non-existing element. Does not change the length
6115// property.
6116// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006117static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00006118 ASSERT(args.length() == 2);
6119 CONVERT_CHECKED(JSObject, object, args[0]);
6120 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
6121 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006122}
6123
6124
6125// Move contents of argument 0 (an array) to argument 1 (an array)
6126static Object* Runtime_MoveArrayContents(Arguments args) {
6127 ASSERT(args.length() == 2);
6128 CONVERT_CHECKED(JSArray, from, args[0]);
6129 CONVERT_CHECKED(JSArray, to, args[1]);
6130 to->SetContent(FixedArray::cast(from->elements()));
6131 to->set_length(from->length());
6132 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006133 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006134 return to;
6135}
6136
6137
6138// How many elements does this array have?
6139static Object* Runtime_EstimateNumberOfElements(Arguments args) {
6140 ASSERT(args.length() == 1);
6141 CONVERT_CHECKED(JSArray, array, args[0]);
6142 HeapObject* elements = array->elements();
6143 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006144 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006145 } else {
6146 return array->length();
6147 }
6148}
6149
6150
6151// Returns an array that tells you where in the [0, length) interval an array
6152// might have elements. Can either return keys or intervals. Keys can have
6153// gaps in (undefined). Intervals can also span over some undefined keys.
6154static Object* Runtime_GetArrayKeys(Arguments args) {
6155 ASSERT(args.length() == 2);
6156 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00006157 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006158 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006159 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006160 // Create an array and get all the keys into it, then remove all the
6161 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006162 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006163 int keys_length = keys->length();
6164 for (int i = 0; i < keys_length; i++) {
6165 Object* key = keys->get(i);
6166 uint32_t index;
6167 if (!Array::IndexFromObject(key, &index) || index >= length) {
6168 // Zap invalid keys.
6169 keys->set_undefined(i);
6170 }
6171 }
6172 return *Factory::NewJSArrayWithElements(keys);
6173 } else {
6174 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
6175 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006176 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00006177 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
6178 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006179 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00006180 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006181 single_interval->set(1, *length_object);
6182 return *Factory::NewJSArrayWithElements(single_interval);
6183 }
6184}
6185
6186
6187// DefineAccessor takes an optional final argument which is the
6188// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
6189// to the way accessors are implemented, it is set for both the getter
6190// and setter on the first call to DefineAccessor and ignored on
6191// subsequent calls.
6192static Object* Runtime_DefineAccessor(Arguments args) {
6193 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
6194 // Compute attributes.
6195 PropertyAttributes attributes = NONE;
6196 if (args.length() == 5) {
6197 CONVERT_CHECKED(Smi, attrs, args[4]);
6198 int value = attrs->value();
6199 // Only attribute bits should be set.
6200 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
6201 attributes = static_cast<PropertyAttributes>(value);
6202 }
6203
6204 CONVERT_CHECKED(JSObject, obj, args[0]);
6205 CONVERT_CHECKED(String, name, args[1]);
6206 CONVERT_CHECKED(Smi, flag, args[2]);
6207 CONVERT_CHECKED(JSFunction, fun, args[3]);
6208 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
6209}
6210
6211
6212static Object* Runtime_LookupAccessor(Arguments args) {
6213 ASSERT(args.length() == 3);
6214 CONVERT_CHECKED(JSObject, obj, args[0]);
6215 CONVERT_CHECKED(String, name, args[1]);
6216 CONVERT_CHECKED(Smi, flag, args[2]);
6217 return obj->LookupAccessor(name, flag->value() == 0);
6218}
6219
6220
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006221#ifdef ENABLE_DEBUGGER_SUPPORT
6222static Object* Runtime_DebugBreak(Arguments args) {
6223 ASSERT(args.length() == 0);
6224 return Execution::DebugBreakHelper();
6225}
6226
6227
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006228// Helper functions for wrapping and unwrapping stack frame ids.
6229static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006230 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006231 return Smi::FromInt(id >> 2);
6232}
6233
6234
6235static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
6236 return static_cast<StackFrame::Id>(wrapped->value() << 2);
6237}
6238
6239
6240// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00006241// args[0]: debug event listener function to set or null or undefined for
6242// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006243// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00006244static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006245 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006246 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
6247 args[0]->IsUndefined() ||
6248 args[0]->IsNull());
6249 Handle<Object> callback = args.at<Object>(0);
6250 Handle<Object> data = args.at<Object>(1);
6251 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006252
6253 return Heap::undefined_value();
6254}
6255
6256
6257static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006258 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006259 StackGuard::DebugBreak();
6260 return Heap::undefined_value();
6261}
6262
6263
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006264static Object* DebugLookupResultValue(Object* receiver, String* name,
6265 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00006266 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006267 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006268 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006269 case NORMAL:
6270 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006271 if (value->IsTheHole()) {
6272 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006273 }
6274 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006275 case FIELD:
6276 value =
6277 JSObject::cast(
6278 result->holder())->FastPropertyAt(result->GetFieldIndex());
6279 if (value->IsTheHole()) {
6280 return Heap::undefined_value();
6281 }
6282 return value;
6283 case CONSTANT_FUNCTION:
6284 return result->GetConstantFunction();
6285 case CALLBACKS: {
6286 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006287 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006288 value = receiver->GetPropertyWithCallback(
6289 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00006290 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006291 value = Top::pending_exception();
6292 Top::clear_pending_exception();
6293 if (caught_exception != NULL) {
6294 *caught_exception = true;
6295 }
6296 }
6297 return value;
6298 } else {
6299 return Heap::undefined_value();
6300 }
6301 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006302 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006303 case MAP_TRANSITION:
6304 case CONSTANT_TRANSITION:
6305 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006306 return Heap::undefined_value();
6307 default:
6308 UNREACHABLE();
6309 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006310 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006311 return Heap::undefined_value();
6312}
6313
6314
ager@chromium.org32912102009-01-16 10:38:43 +00006315// Get debugger related details for an object property.
6316// args[0]: object holding property
6317// args[1]: name of the property
6318//
6319// The array returned contains the following information:
6320// 0: Property value
6321// 1: Property details
6322// 2: Property value is exception
6323// 3: Getter function if defined
6324// 4: Setter function if defined
6325// Items 2-4 are only filled if the property has either a getter or a setter
6326// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006327static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006328 HandleScope scope;
6329
6330 ASSERT(args.length() == 2);
6331
6332 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6333 CONVERT_ARG_CHECKED(String, name, 1);
6334
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006335 // Make sure to set the current context to the context before the debugger was
6336 // entered (if the debugger is entered). The reason for switching context here
6337 // is that for some property lookups (accessors and interceptors) callbacks
6338 // into the embedding application can occour, and the embedding application
6339 // could have the assumption that its own global context is the current
6340 // context and not some internal debugger context.
6341 SaveContext save;
6342 if (Debug::InDebugger()) {
6343 Top::set_context(*Debug::debugger_entry()->GetContext());
6344 }
6345
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006346 // Skip the global proxy as it has no properties and always delegates to the
6347 // real global object.
6348 if (obj->IsJSGlobalProxy()) {
6349 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6350 }
6351
6352
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006353 // Check if the name is trivially convertible to an index and get the element
6354 // if so.
6355 uint32_t index;
6356 if (name->AsArrayIndex(&index)) {
6357 Handle<FixedArray> details = Factory::NewFixedArray(2);
6358 details->set(0, Runtime::GetElementOrCharAt(obj, index));
6359 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
6360 return *Factory::NewJSArrayWithElements(details);
6361 }
6362
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006363 // Find the number of objects making up this.
6364 int length = LocalPrototypeChainLength(*obj);
6365
6366 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006367 Handle<JSObject> jsproto = obj;
6368 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006369 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006370 jsproto->LocalLookup(*name, &result);
6371 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006372 // LookupResult is not GC safe as it holds raw object pointers.
6373 // GC can happen later in this code so put the required fields into
6374 // local variables using handles when required for later use.
6375 PropertyType result_type = result.type();
6376 Handle<Object> result_callback_obj;
6377 if (result_type == CALLBACKS) {
6378 result_callback_obj = Handle<Object>(result.GetCallbackObject());
6379 }
6380 Smi* property_details = result.GetPropertyDetails().AsSmi();
6381 // DebugLookupResultValue can cause GC so details from LookupResult needs
6382 // to be copied to handles before this.
6383 bool caught_exception = false;
6384 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
6385 &caught_exception);
6386 if (raw_value->IsFailure()) return raw_value;
6387 Handle<Object> value(raw_value);
6388
6389 // If the callback object is a fixed array then it contains JavaScript
6390 // getter and/or setter.
6391 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
6392 result_callback_obj->IsFixedArray();
6393 Handle<FixedArray> details =
6394 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
6395 details->set(0, *value);
6396 details->set(1, property_details);
6397 if (hasJavaScriptAccessors) {
6398 details->set(2,
6399 caught_exception ? Heap::true_value()
6400 : Heap::false_value());
6401 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
6402 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
6403 }
6404
6405 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006406 }
6407 if (i < length - 1) {
6408 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6409 }
6410 }
6411
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006412 return Heap::undefined_value();
6413}
6414
6415
6416static Object* Runtime_DebugGetProperty(Arguments args) {
6417 HandleScope scope;
6418
6419 ASSERT(args.length() == 2);
6420
6421 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6422 CONVERT_ARG_CHECKED(String, name, 1);
6423
6424 LookupResult result;
6425 obj->Lookup(*name, &result);
6426 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006427 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006428 }
6429 return Heap::undefined_value();
6430}
6431
6432
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006433// Return the property type calculated from the property details.
6434// args[0]: smi with property details.
6435static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6436 ASSERT(args.length() == 1);
6437 CONVERT_CHECKED(Smi, details, args[0]);
6438 PropertyType type = PropertyDetails(details).type();
6439 return Smi::FromInt(static_cast<int>(type));
6440}
6441
6442
6443// Return the property attribute calculated from the property details.
6444// args[0]: smi with property details.
6445static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6446 ASSERT(args.length() == 1);
6447 CONVERT_CHECKED(Smi, details, args[0]);
6448 PropertyAttributes attributes = PropertyDetails(details).attributes();
6449 return Smi::FromInt(static_cast<int>(attributes));
6450}
6451
6452
6453// Return the property insertion index calculated from the property details.
6454// args[0]: smi with property details.
6455static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6456 ASSERT(args.length() == 1);
6457 CONVERT_CHECKED(Smi, details, args[0]);
6458 int index = PropertyDetails(details).index();
6459 return Smi::FromInt(index);
6460}
6461
6462
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006463// Return property value from named interceptor.
6464// args[0]: object
6465// args[1]: property name
6466static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6467 HandleScope scope;
6468 ASSERT(args.length() == 2);
6469 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6470 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6471 CONVERT_ARG_CHECKED(String, name, 1);
6472
6473 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006474 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006475}
6476
6477
6478// Return element value from indexed interceptor.
6479// args[0]: object
6480// args[1]: index
6481static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6482 HandleScope scope;
6483 ASSERT(args.length() == 2);
6484 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6485 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6486 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6487
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006488 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006489}
6490
6491
6492static Object* Runtime_CheckExecutionState(Arguments args) {
6493 ASSERT(args.length() >= 1);
6494 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006495 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006496 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006497 return Top::Throw(Heap::illegal_execution_state_symbol());
6498 }
6499
6500 return Heap::true_value();
6501}
6502
6503
6504static Object* Runtime_GetFrameCount(Arguments args) {
6505 HandleScope scope;
6506 ASSERT(args.length() == 1);
6507
6508 // Check arguments.
6509 Object* result = Runtime_CheckExecutionState(args);
6510 if (result->IsFailure()) return result;
6511
6512 // Count all frames which are relevant to debugging stack trace.
6513 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006514 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006515 if (id == StackFrame::NO_ID) {
6516 // If there is no JavaScript stack frame count is 0.
6517 return Smi::FromInt(0);
6518 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006519 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6520 return Smi::FromInt(n);
6521}
6522
6523
6524static const int kFrameDetailsFrameIdIndex = 0;
6525static const int kFrameDetailsReceiverIndex = 1;
6526static const int kFrameDetailsFunctionIndex = 2;
6527static const int kFrameDetailsArgumentCountIndex = 3;
6528static const int kFrameDetailsLocalCountIndex = 4;
6529static const int kFrameDetailsSourcePositionIndex = 5;
6530static const int kFrameDetailsConstructCallIndex = 6;
6531static const int kFrameDetailsDebuggerFrameIndex = 7;
6532static const int kFrameDetailsFirstDynamicIndex = 8;
6533
6534// Return an array with frame details
6535// args[0]: number: break id
6536// args[1]: number: frame index
6537//
6538// The array returned contains the following information:
6539// 0: Frame id
6540// 1: Receiver
6541// 2: Function
6542// 3: Argument count
6543// 4: Local count
6544// 5: Source position
6545// 6: Constructor call
6546// 7: Debugger frame
6547// Arguments name, value
6548// Locals name, value
6549static Object* Runtime_GetFrameDetails(Arguments args) {
6550 HandleScope scope;
6551 ASSERT(args.length() == 2);
6552
6553 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006554 Object* check = Runtime_CheckExecutionState(args);
6555 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006556 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6557
6558 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006559 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006560 if (id == StackFrame::NO_ID) {
6561 // If there are no JavaScript stack frames return undefined.
6562 return Heap::undefined_value();
6563 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006564 int count = 0;
6565 JavaScriptFrameIterator it(id);
6566 for (; !it.done(); it.Advance()) {
6567 if (count == index) break;
6568 count++;
6569 }
6570 if (it.done()) return Heap::undefined_value();
6571
6572 // Traverse the saved contexts chain to find the active context for the
6573 // selected frame.
6574 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006575 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006576 save = save->prev();
6577 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006578 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006579
6580 // Get the frame id.
6581 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6582
6583 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006584 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006585
6586 // Check for constructor frame.
6587 bool constructor = it.frame()->IsConstructor();
6588
6589 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006590 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006591 ScopeInfo<> info(*code);
6592
6593 // Get the context.
6594 Handle<Context> context(Context::cast(it.frame()->context()));
6595
6596 // Get the locals names and values into a temporary array.
6597 //
6598 // TODO(1240907): Hide compiler-introduced stack variables
6599 // (e.g. .result)? For users of the debugger, they will probably be
6600 // confusing.
6601 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6602 for (int i = 0; i < info.NumberOfLocals(); i++) {
6603 // Name of the local.
6604 locals->set(i * 2, *info.LocalName(i));
6605
6606 // Fetch the value of the local - either from the stack or from a
6607 // heap-allocated context.
6608 if (i < info.number_of_stack_slots()) {
6609 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6610 } else {
6611 Handle<String> name = info.LocalName(i);
6612 // Traverse the context chain to the function context as all local
6613 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006614 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006615 context = Handle<Context>(context->previous());
6616 }
6617 ASSERT(context->is_function_context());
6618 locals->set(i * 2 + 1,
6619 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6620 NULL)));
6621 }
6622 }
6623
6624 // Now advance to the arguments adapter frame (if any). If contains all
6625 // the provided parameters and
6626
6627 // Now advance to the arguments adapter frame (if any). It contains all
6628 // the provided parameters whereas the function frame always have the number
6629 // of arguments matching the functions parameters. The rest of the
6630 // information (except for what is collected above) is the same.
6631 it.AdvanceToArgumentsFrame();
6632
6633 // Find the number of arguments to fill. At least fill the number of
6634 // parameters for the function and fill more if more parameters are provided.
6635 int argument_count = info.number_of_parameters();
6636 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6637 argument_count = it.frame()->GetProvidedParametersCount();
6638 }
6639
6640 // Calculate the size of the result.
6641 int details_size = kFrameDetailsFirstDynamicIndex +
6642 2 * (argument_count + info.NumberOfLocals());
6643 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6644
6645 // Add the frame id.
6646 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6647
6648 // Add the function (same as in function frame).
6649 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6650
6651 // Add the arguments count.
6652 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6653
6654 // Add the locals count
6655 details->set(kFrameDetailsLocalCountIndex,
6656 Smi::FromInt(info.NumberOfLocals()));
6657
6658 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006659 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006660 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6661 } else {
6662 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6663 }
6664
6665 // Add the constructor information.
6666 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6667
6668 // Add information on whether this frame is invoked in the debugger context.
6669 details->set(kFrameDetailsDebuggerFrameIndex,
6670 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6671
6672 // Fill the dynamic part.
6673 int details_index = kFrameDetailsFirstDynamicIndex;
6674
6675 // Add arguments name and value.
6676 for (int i = 0; i < argument_count; i++) {
6677 // Name of the argument.
6678 if (i < info.number_of_parameters()) {
6679 details->set(details_index++, *info.parameter_name(i));
6680 } else {
6681 details->set(details_index++, Heap::undefined_value());
6682 }
6683
6684 // Parameter value.
6685 if (i < it.frame()->GetProvidedParametersCount()) {
6686 details->set(details_index++, it.frame()->GetParameter(i));
6687 } else {
6688 details->set(details_index++, Heap::undefined_value());
6689 }
6690 }
6691
6692 // Add locals name and value from the temporary copy from the function frame.
6693 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6694 details->set(details_index++, locals->get(i));
6695 }
6696
6697 // Add the receiver (same as in function frame).
6698 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6699 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6700 Handle<Object> receiver(it.frame()->receiver());
6701 if (!receiver->IsJSObject()) {
6702 // If the receiver is NOT a JSObject we have hit an optimization
6703 // where a value object is not converted into a wrapped JS objects.
6704 // To hide this optimization from the debugger, we wrap the receiver
6705 // by creating correct wrapper object based on the calling frame's
6706 // global context.
6707 it.Advance();
6708 Handle<Context> calling_frames_global_context(
6709 Context::cast(Context::cast(it.frame()->context())->global_context()));
6710 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6711 }
6712 details->set(kFrameDetailsReceiverIndex, *receiver);
6713
6714 ASSERT_EQ(details_size, details_index);
6715 return *Factory::NewJSArrayWithElements(details);
6716}
6717
6718
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006719// Copy all the context locals into an object used to materialize a scope.
6720static void CopyContextLocalsToScopeObject(Handle<Code> code,
6721 ScopeInfo<>& scope_info,
6722 Handle<Context> context,
6723 Handle<JSObject> scope_object) {
6724 // Fill all context locals to the context extension.
6725 for (int i = Context::MIN_CONTEXT_SLOTS;
6726 i < scope_info.number_of_context_slots();
6727 i++) {
6728 int context_index =
6729 ScopeInfo<>::ContextSlotIndex(*code,
6730 *scope_info.context_slot_name(i),
6731 NULL);
6732
6733 // Don't include the arguments shadow (.arguments) context variable.
6734 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6735 SetProperty(scope_object,
6736 scope_info.context_slot_name(i),
6737 Handle<Object>(context->get(context_index)), NONE);
6738 }
6739 }
6740}
6741
6742
6743// Create a plain JSObject which materializes the local scope for the specified
6744// frame.
6745static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6746 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6747 Handle<Code> code(function->code());
6748 ScopeInfo<> scope_info(*code);
6749
6750 // Allocate and initialize a JSObject with all the arguments, stack locals
6751 // heap locals and extension properties of the debugged function.
6752 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6753
6754 // First fill all parameters.
6755 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6756 SetProperty(local_scope,
6757 scope_info.parameter_name(i),
6758 Handle<Object>(frame->GetParameter(i)), NONE);
6759 }
6760
6761 // Second fill all stack locals.
6762 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6763 SetProperty(local_scope,
6764 scope_info.stack_slot_name(i),
6765 Handle<Object>(frame->GetExpression(i)), NONE);
6766 }
6767
6768 // Third fill all context locals.
6769 Handle<Context> frame_context(Context::cast(frame->context()));
6770 Handle<Context> function_context(frame_context->fcontext());
6771 CopyContextLocalsToScopeObject(code, scope_info,
6772 function_context, local_scope);
6773
6774 // Finally copy any properties from the function context extension. This will
6775 // be variables introduced by eval.
6776 if (function_context->closure() == *function) {
6777 if (function_context->has_extension() &&
6778 !function_context->IsGlobalContext()) {
6779 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006780 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006781 for (int i = 0; i < keys->length(); i++) {
6782 // Names of variables introduced by eval are strings.
6783 ASSERT(keys->get(i)->IsString());
6784 Handle<String> key(String::cast(keys->get(i)));
6785 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6786 }
6787 }
6788 }
6789 return local_scope;
6790}
6791
6792
6793// Create a plain JSObject which materializes the closure content for the
6794// context.
6795static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6796 ASSERT(context->is_function_context());
6797
6798 Handle<Code> code(context->closure()->code());
6799 ScopeInfo<> scope_info(*code);
6800
6801 // Allocate and initialize a JSObject with all the content of theis function
6802 // closure.
6803 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6804
6805 // Check whether the arguments shadow object exists.
6806 int arguments_shadow_index =
6807 ScopeInfo<>::ContextSlotIndex(*code,
6808 Heap::arguments_shadow_symbol(),
6809 NULL);
6810 if (arguments_shadow_index >= 0) {
6811 // In this case all the arguments are available in the arguments shadow
6812 // object.
6813 Handle<JSObject> arguments_shadow(
6814 JSObject::cast(context->get(arguments_shadow_index)));
6815 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6816 SetProperty(closure_scope,
6817 scope_info.parameter_name(i),
6818 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6819 }
6820 }
6821
6822 // Fill all context locals to the context extension.
6823 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6824
6825 // Finally copy any properties from the function context extension. This will
6826 // be variables introduced by eval.
6827 if (context->has_extension()) {
6828 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006829 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006830 for (int i = 0; i < keys->length(); i++) {
6831 // Names of variables introduced by eval are strings.
6832 ASSERT(keys->get(i)->IsString());
6833 Handle<String> key(String::cast(keys->get(i)));
6834 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6835 }
6836 }
6837
6838 return closure_scope;
6839}
6840
6841
6842// Iterate over the actual scopes visible from a stack frame. All scopes are
6843// backed by an actual context except the local scope, which is inserted
6844// "artifically" in the context chain.
6845class ScopeIterator {
6846 public:
6847 enum ScopeType {
6848 ScopeTypeGlobal = 0,
6849 ScopeTypeLocal,
6850 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006851 ScopeTypeClosure,
6852 // Every catch block contains an implicit with block (its parameter is
6853 // a JSContextExtensionObject) that extends current scope with a variable
6854 // holding exception object. Such with blocks are treated as scopes of their
6855 // own type.
6856 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006857 };
6858
6859 explicit ScopeIterator(JavaScriptFrame* frame)
6860 : frame_(frame),
6861 function_(JSFunction::cast(frame->function())),
6862 context_(Context::cast(frame->context())),
6863 local_done_(false),
6864 at_local_(false) {
6865
6866 // Check whether the first scope is actually a local scope.
6867 if (context_->IsGlobalContext()) {
6868 // If there is a stack slot for .result then this local scope has been
6869 // created for evaluating top level code and it is not a real local scope.
6870 // Checking for the existence of .result seems fragile, but the scope info
6871 // saved with the code object does not otherwise have that information.
6872 Handle<Code> code(function_->code());
6873 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6874 at_local_ = index < 0;
6875 } else if (context_->is_function_context()) {
6876 at_local_ = true;
6877 }
6878 }
6879
6880 // More scopes?
6881 bool Done() { return context_.is_null(); }
6882
6883 // Move to the next scope.
6884 void Next() {
6885 // If at a local scope mark the local scope as passed.
6886 if (at_local_) {
6887 at_local_ = false;
6888 local_done_ = true;
6889
6890 // If the current context is not associated with the local scope the
6891 // current context is the next real scope, so don't move to the next
6892 // context in this case.
6893 if (context_->closure() != *function_) {
6894 return;
6895 }
6896 }
6897
6898 // The global scope is always the last in the chain.
6899 if (context_->IsGlobalContext()) {
6900 context_ = Handle<Context>();
6901 return;
6902 }
6903
6904 // Move to the next context.
6905 if (context_->is_function_context()) {
6906 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6907 } else {
6908 context_ = Handle<Context>(context_->previous());
6909 }
6910
6911 // If passing the local scope indicate that the current scope is now the
6912 // local scope.
6913 if (!local_done_ &&
6914 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6915 at_local_ = true;
6916 }
6917 }
6918
6919 // Return the type of the current scope.
6920 int Type() {
6921 if (at_local_) {
6922 return ScopeTypeLocal;
6923 }
6924 if (context_->IsGlobalContext()) {
6925 ASSERT(context_->global()->IsGlobalObject());
6926 return ScopeTypeGlobal;
6927 }
6928 if (context_->is_function_context()) {
6929 return ScopeTypeClosure;
6930 }
6931 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006932 // Current scope is either an explicit with statement or a with statement
6933 // implicitely generated for a catch block.
6934 // If the extension object here is a JSContextExtensionObject then
6935 // current with statement is one frome a catch block otherwise it's a
6936 // regular with statement.
6937 if (context_->extension()->IsJSContextExtensionObject()) {
6938 return ScopeTypeCatch;
6939 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006940 return ScopeTypeWith;
6941 }
6942
6943 // Return the JavaScript object with the content of the current scope.
6944 Handle<JSObject> ScopeObject() {
6945 switch (Type()) {
6946 case ScopeIterator::ScopeTypeGlobal:
6947 return Handle<JSObject>(CurrentContext()->global());
6948 break;
6949 case ScopeIterator::ScopeTypeLocal:
6950 // Materialize the content of the local scope into a JSObject.
6951 return MaterializeLocalScope(frame_);
6952 break;
6953 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006954 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006955 // Return the with object.
6956 return Handle<JSObject>(CurrentContext()->extension());
6957 break;
6958 case ScopeIterator::ScopeTypeClosure:
6959 // Materialize the content of the closure scope into a JSObject.
6960 return MaterializeClosure(CurrentContext());
6961 break;
6962 }
6963 UNREACHABLE();
6964 return Handle<JSObject>();
6965 }
6966
6967 // Return the context for this scope. For the local context there might not
6968 // be an actual context.
6969 Handle<Context> CurrentContext() {
6970 if (at_local_ && context_->closure() != *function_) {
6971 return Handle<Context>();
6972 }
6973 return context_;
6974 }
6975
6976#ifdef DEBUG
6977 // Debug print of the content of the current scope.
6978 void DebugPrint() {
6979 switch (Type()) {
6980 case ScopeIterator::ScopeTypeGlobal:
6981 PrintF("Global:\n");
6982 CurrentContext()->Print();
6983 break;
6984
6985 case ScopeIterator::ScopeTypeLocal: {
6986 PrintF("Local:\n");
6987 Handle<Code> code(function_->code());
6988 ScopeInfo<> scope_info(*code);
6989 scope_info.Print();
6990 if (!CurrentContext().is_null()) {
6991 CurrentContext()->Print();
6992 if (CurrentContext()->has_extension()) {
6993 Handle<JSObject> extension =
6994 Handle<JSObject>(CurrentContext()->extension());
6995 if (extension->IsJSContextExtensionObject()) {
6996 extension->Print();
6997 }
6998 }
6999 }
7000 break;
7001 }
7002
7003 case ScopeIterator::ScopeTypeWith: {
7004 PrintF("With:\n");
7005 Handle<JSObject> extension =
7006 Handle<JSObject>(CurrentContext()->extension());
7007 extension->Print();
7008 break;
7009 }
7010
ager@chromium.orga1645e22009-09-09 19:27:10 +00007011 case ScopeIterator::ScopeTypeCatch: {
7012 PrintF("Catch:\n");
7013 Handle<JSObject> extension =
7014 Handle<JSObject>(CurrentContext()->extension());
7015 extension->Print();
7016 break;
7017 }
7018
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007019 case ScopeIterator::ScopeTypeClosure: {
7020 PrintF("Closure:\n");
7021 CurrentContext()->Print();
7022 if (CurrentContext()->has_extension()) {
7023 Handle<JSObject> extension =
7024 Handle<JSObject>(CurrentContext()->extension());
7025 if (extension->IsJSContextExtensionObject()) {
7026 extension->Print();
7027 }
7028 }
7029 break;
7030 }
7031
7032 default:
7033 UNREACHABLE();
7034 }
7035 PrintF("\n");
7036 }
7037#endif
7038
7039 private:
7040 JavaScriptFrame* frame_;
7041 Handle<JSFunction> function_;
7042 Handle<Context> context_;
7043 bool local_done_;
7044 bool at_local_;
7045
7046 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
7047};
7048
7049
7050static Object* Runtime_GetScopeCount(Arguments args) {
7051 HandleScope scope;
7052 ASSERT(args.length() == 2);
7053
7054 // Check arguments.
7055 Object* check = Runtime_CheckExecutionState(args);
7056 if (check->IsFailure()) return check;
7057 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7058
7059 // Get the frame where the debugging is performed.
7060 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7061 JavaScriptFrameIterator it(id);
7062 JavaScriptFrame* frame = it.frame();
7063
7064 // Count the visible scopes.
7065 int n = 0;
7066 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
7067 n++;
7068 }
7069
7070 return Smi::FromInt(n);
7071}
7072
7073
7074static const int kScopeDetailsTypeIndex = 0;
7075static const int kScopeDetailsObjectIndex = 1;
7076static const int kScopeDetailsSize = 2;
7077
7078// Return an array with scope details
7079// args[0]: number: break id
7080// args[1]: number: frame index
7081// args[2]: number: scope index
7082//
7083// The array returned contains the following information:
7084// 0: Scope type
7085// 1: Scope object
7086static Object* Runtime_GetScopeDetails(Arguments args) {
7087 HandleScope scope;
7088 ASSERT(args.length() == 3);
7089
7090 // Check arguments.
7091 Object* check = Runtime_CheckExecutionState(args);
7092 if (check->IsFailure()) return check;
7093 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7094 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
7095
7096 // Get the frame where the debugging is performed.
7097 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7098 JavaScriptFrameIterator frame_it(id);
7099 JavaScriptFrame* frame = frame_it.frame();
7100
7101 // Find the requested scope.
7102 int n = 0;
7103 ScopeIterator it(frame);
7104 for (; !it.Done() && n < index; it.Next()) {
7105 n++;
7106 }
7107 if (it.Done()) {
7108 return Heap::undefined_value();
7109 }
7110
7111 // Calculate the size of the result.
7112 int details_size = kScopeDetailsSize;
7113 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
7114
7115 // Fill in scope details.
7116 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
7117 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
7118
7119 return *Factory::NewJSArrayWithElements(details);
7120}
7121
7122
7123static Object* Runtime_DebugPrintScopes(Arguments args) {
7124 HandleScope scope;
7125 ASSERT(args.length() == 0);
7126
7127#ifdef DEBUG
7128 // Print the scopes for the top frame.
7129 StackFrameLocator locator;
7130 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
7131 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
7132 it.DebugPrint();
7133 }
7134#endif
7135 return Heap::undefined_value();
7136}
7137
7138
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007139static Object* Runtime_GetCFrames(Arguments args) {
7140 HandleScope scope;
7141 ASSERT(args.length() == 1);
7142 Object* result = Runtime_CheckExecutionState(args);
7143 if (result->IsFailure()) return result;
7144
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007145#if V8_HOST_ARCH_64_BIT
7146 UNIMPLEMENTED();
7147 return Heap::undefined_value();
7148#else
7149
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007150 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007151 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
7152 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007153 if (frames_count == OS::kStackWalkError) {
7154 return Heap::undefined_value();
7155 }
7156
7157 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
7158 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
7159 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
7160 for (int i = 0; i < frames_count; i++) {
7161 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
7162 frame_value->SetProperty(
7163 *address_str,
7164 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
7165 NONE);
7166
7167 // Get the stack walk text for this frame.
7168 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007169 int frame_text_length = StrLength(frames[i].text);
7170 if (frame_text_length > 0) {
7171 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007172 frame_text = Factory::NewStringFromAscii(str);
7173 }
7174
7175 if (!frame_text.is_null()) {
7176 frame_value->SetProperty(*text_str, *frame_text, NONE);
7177 }
7178
7179 frames_array->set(i, *frame_value);
7180 }
7181 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007182#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007183}
7184
7185
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007186static Object* Runtime_GetThreadCount(Arguments args) {
7187 HandleScope scope;
7188 ASSERT(args.length() == 1);
7189
7190 // Check arguments.
7191 Object* result = Runtime_CheckExecutionState(args);
7192 if (result->IsFailure()) return result;
7193
7194 // Count all archived V8 threads.
7195 int n = 0;
7196 for (ThreadState* thread = ThreadState::FirstInUse();
7197 thread != NULL;
7198 thread = thread->Next()) {
7199 n++;
7200 }
7201
7202 // Total number of threads is current thread and archived threads.
7203 return Smi::FromInt(n + 1);
7204}
7205
7206
7207static const int kThreadDetailsCurrentThreadIndex = 0;
7208static const int kThreadDetailsThreadIdIndex = 1;
7209static const int kThreadDetailsSize = 2;
7210
7211// Return an array with thread details
7212// args[0]: number: break id
7213// args[1]: number: thread index
7214//
7215// The array returned contains the following information:
7216// 0: Is current thread?
7217// 1: Thread id
7218static Object* Runtime_GetThreadDetails(Arguments args) {
7219 HandleScope scope;
7220 ASSERT(args.length() == 2);
7221
7222 // Check arguments.
7223 Object* check = Runtime_CheckExecutionState(args);
7224 if (check->IsFailure()) return check;
7225 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
7226
7227 // Allocate array for result.
7228 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
7229
7230 // Thread index 0 is current thread.
7231 if (index == 0) {
7232 // Fill the details.
7233 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
7234 details->set(kThreadDetailsThreadIdIndex,
7235 Smi::FromInt(ThreadManager::CurrentId()));
7236 } else {
7237 // Find the thread with the requested index.
7238 int n = 1;
7239 ThreadState* thread = ThreadState::FirstInUse();
7240 while (index != n && thread != NULL) {
7241 thread = thread->Next();
7242 n++;
7243 }
7244 if (thread == NULL) {
7245 return Heap::undefined_value();
7246 }
7247
7248 // Fill the details.
7249 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
7250 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
7251 }
7252
7253 // Convert to JS array and return.
7254 return *Factory::NewJSArrayWithElements(details);
7255}
7256
7257
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007258static Object* Runtime_GetBreakLocations(Arguments args) {
7259 HandleScope scope;
7260 ASSERT(args.length() == 1);
7261
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007262 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7263 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007264 // Find the number of break points
7265 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
7266 if (break_locations->IsUndefined()) return Heap::undefined_value();
7267 // Return array as JS array
7268 return *Factory::NewJSArrayWithElements(
7269 Handle<FixedArray>::cast(break_locations));
7270}
7271
7272
7273// Set a break point in a function
7274// args[0]: function
7275// args[1]: number: break source position (within the function source)
7276// args[2]: number: break point object
7277static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
7278 HandleScope scope;
7279 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007280 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7281 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007282 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7283 RUNTIME_ASSERT(source_position >= 0);
7284 Handle<Object> break_point_object_arg = args.at<Object>(2);
7285
7286 // Set break point.
7287 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
7288
7289 return Heap::undefined_value();
7290}
7291
7292
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007293Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
7294 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007295 // Iterate the heap looking for SharedFunctionInfo generated from the
7296 // script. The inner most SharedFunctionInfo containing the source position
7297 // for the requested break point is found.
7298 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
7299 // which is found is not compiled it is compiled and the heap is iterated
7300 // again as the compilation might create inner functions from the newly
7301 // compiled function and the actual requested break point might be in one of
7302 // these functions.
7303 bool done = false;
7304 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00007305 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007306 Handle<SharedFunctionInfo> target;
7307 // The current candidate for the last function in script:
7308 Handle<SharedFunctionInfo> last;
7309 while (!done) {
7310 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007311 for (HeapObject* obj = iterator.next();
7312 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007313 if (obj->IsSharedFunctionInfo()) {
7314 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
7315 if (shared->script() == *script) {
7316 // If the SharedFunctionInfo found has the requested script data and
7317 // contains the source position it is a candidate.
7318 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00007319 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007320 start_position = shared->start_position();
7321 }
7322 if (start_position <= position &&
7323 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007324 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007325 // candidate this is the new candidate.
7326 if (target.is_null()) {
7327 target_start_position = start_position;
7328 target = shared;
7329 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007330 if (target_start_position == start_position &&
7331 shared->end_position() == target->end_position()) {
7332 // If a top-level function contain only one function
7333 // declartion the source for the top-level and the function is
7334 // the same. In that case prefer the non top-level function.
7335 if (!shared->is_toplevel()) {
7336 target_start_position = start_position;
7337 target = shared;
7338 }
7339 } else if (target_start_position <= start_position &&
7340 shared->end_position() <= target->end_position()) {
7341 // This containment check includes equality as a function inside
7342 // a top-level function can share either start or end position
7343 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007344 target_start_position = start_position;
7345 target = shared;
7346 }
7347 }
7348 }
7349
7350 // Keep track of the last function in the script.
7351 if (last.is_null() ||
7352 shared->end_position() > last->start_position()) {
7353 last = shared;
7354 }
7355 }
7356 }
7357 }
7358
7359 // Make sure some candidate is selected.
7360 if (target.is_null()) {
7361 if (!last.is_null()) {
7362 // Position after the last function - use last.
7363 target = last;
7364 } else {
7365 // Unable to find function - possibly script without any function.
7366 return Heap::undefined_value();
7367 }
7368 }
7369
7370 // If the candidate found is compiled we are done. NOTE: when lazy
7371 // compilation of inner functions is introduced some additional checking
7372 // needs to be done here to compile inner functions.
7373 done = target->is_compiled();
7374 if (!done) {
7375 // If the candidate is not compiled compile it to reveal any inner
7376 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007377 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007378 }
7379 }
7380
7381 return *target;
7382}
7383
7384
7385// Change the state of a break point in a script. NOTE: Regarding performance
7386// see the NOTE for GetScriptFromScriptData.
7387// args[0]: script to set break point in
7388// args[1]: number: break source position (within the script source)
7389// args[2]: number: break point object
7390static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7391 HandleScope scope;
7392 ASSERT(args.length() == 3);
7393 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7394 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7395 RUNTIME_ASSERT(source_position >= 0);
7396 Handle<Object> break_point_object_arg = args.at<Object>(2);
7397
7398 // Get the script from the script wrapper.
7399 RUNTIME_ASSERT(wrapper->value()->IsScript());
7400 Handle<Script> script(Script::cast(wrapper->value()));
7401
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007402 Object* result = Runtime::FindSharedFunctionInfoInScript(
7403 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007404 if (!result->IsUndefined()) {
7405 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7406 // Find position within function. The script position might be before the
7407 // source position of the first function.
7408 int position;
7409 if (shared->start_position() > source_position) {
7410 position = 0;
7411 } else {
7412 position = source_position - shared->start_position();
7413 }
7414 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7415 }
7416 return Heap::undefined_value();
7417}
7418
7419
7420// Clear a break point
7421// args[0]: number: break point object
7422static Object* Runtime_ClearBreakPoint(Arguments args) {
7423 HandleScope scope;
7424 ASSERT(args.length() == 1);
7425 Handle<Object> break_point_object_arg = args.at<Object>(0);
7426
7427 // Clear break point.
7428 Debug::ClearBreakPoint(break_point_object_arg);
7429
7430 return Heap::undefined_value();
7431}
7432
7433
7434// Change the state of break on exceptions
7435// args[0]: boolean indicating uncaught exceptions
7436// args[1]: boolean indicating on/off
7437static Object* Runtime_ChangeBreakOnException(Arguments args) {
7438 HandleScope scope;
7439 ASSERT(args.length() == 2);
7440 ASSERT(args[0]->IsNumber());
7441 ASSERT(args[1]->IsBoolean());
7442
7443 // Update break point state
7444 ExceptionBreakType type =
7445 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7446 bool enable = args[1]->ToBoolean()->IsTrue();
7447 Debug::ChangeBreakOnException(type, enable);
7448 return Heap::undefined_value();
7449}
7450
7451
7452// Prepare for stepping
7453// args[0]: break id for checking execution state
7454// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007455// args[2]: number of times to perform the step, for step out it is the number
7456// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007457static Object* Runtime_PrepareStep(Arguments args) {
7458 HandleScope scope;
7459 ASSERT(args.length() == 3);
7460 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007461 Object* check = Runtime_CheckExecutionState(args);
7462 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007463 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7464 return Top::Throw(Heap::illegal_argument_symbol());
7465 }
7466
7467 // Get the step action and check validity.
7468 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7469 if (step_action != StepIn &&
7470 step_action != StepNext &&
7471 step_action != StepOut &&
7472 step_action != StepInMin &&
7473 step_action != StepMin) {
7474 return Top::Throw(Heap::illegal_argument_symbol());
7475 }
7476
7477 // Get the number of steps.
7478 int step_count = NumberToInt32(args[2]);
7479 if (step_count < 1) {
7480 return Top::Throw(Heap::illegal_argument_symbol());
7481 }
7482
ager@chromium.orga1645e22009-09-09 19:27:10 +00007483 // Clear all current stepping setup.
7484 Debug::ClearStepping();
7485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007486 // Prepare step.
7487 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7488 return Heap::undefined_value();
7489}
7490
7491
7492// Clear all stepping set by PrepareStep.
7493static Object* Runtime_ClearStepping(Arguments args) {
7494 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007495 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007496 Debug::ClearStepping();
7497 return Heap::undefined_value();
7498}
7499
7500
7501// Creates a copy of the with context chain. The copy of the context chain is
7502// is linked to the function context supplied.
7503static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7504 Handle<Context> function_context) {
7505 // At the bottom of the chain. Return the function context to link to.
7506 if (context_chain->is_function_context()) {
7507 return function_context;
7508 }
7509
7510 // Recursively copy the with contexts.
7511 Handle<Context> previous(context_chain->previous());
7512 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7513 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007514 CopyWithContextChain(function_context, previous),
7515 extension,
7516 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007517}
7518
7519
7520// Helper function to find or create the arguments object for
7521// Runtime_DebugEvaluate.
7522static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7523 Handle<JSFunction> function,
7524 Handle<Code> code,
7525 const ScopeInfo<>* sinfo,
7526 Handle<Context> function_context) {
7527 // Try to find the value of 'arguments' to pass as parameter. If it is not
7528 // found (that is the debugged function does not reference 'arguments' and
7529 // does not support eval) then create an 'arguments' object.
7530 int index;
7531 if (sinfo->number_of_stack_slots() > 0) {
7532 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7533 if (index != -1) {
7534 return Handle<Object>(frame->GetExpression(index));
7535 }
7536 }
7537
7538 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7539 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7540 NULL);
7541 if (index != -1) {
7542 return Handle<Object>(function_context->get(index));
7543 }
7544 }
7545
7546 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007547 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7548 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007549
7550 AssertNoAllocation no_gc;
7551 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007552 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007553 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007554 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007555 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007556 return arguments;
7557}
7558
7559
7560// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007561// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007562// extension part has all the parameters and locals of the function on the
7563// stack frame. A function which calls eval with the code to evaluate is then
7564// compiled in this context and called in this context. As this context
7565// replaces the context of the function on the stack frame a new (empty)
7566// function is created as well to be used as the closure for the context.
7567// This function and the context acts as replacements for the function on the
7568// stack frame presenting the same view of the values of parameters and
7569// local variables as if the piece of JavaScript was evaluated at the point
7570// where the function on the stack frame is currently stopped.
7571static Object* Runtime_DebugEvaluate(Arguments args) {
7572 HandleScope scope;
7573
7574 // Check the execution state and decode arguments frame and source to be
7575 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007576 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007577 Object* check_result = Runtime_CheckExecutionState(args);
7578 if (check_result->IsFailure()) return check_result;
7579 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7580 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007581 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7582
7583 // Handle the processing of break.
7584 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007585
7586 // Get the frame where the debugging is performed.
7587 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7588 JavaScriptFrameIterator it(id);
7589 JavaScriptFrame* frame = it.frame();
7590 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7591 Handle<Code> code(function->code());
7592 ScopeInfo<> sinfo(*code);
7593
7594 // Traverse the saved contexts chain to find the active context for the
7595 // selected frame.
7596 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007597 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007598 save = save->prev();
7599 }
7600 ASSERT(save != NULL);
7601 SaveContext savex;
7602 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007603
7604 // Create the (empty) function replacing the function on the stack frame for
7605 // the purpose of evaluating in the context created below. It is important
7606 // that this function does not describe any parameters and local variables
7607 // in the context. If it does then this will cause problems with the lookup
7608 // in Context::Lookup, where context slots for parameters and local variables
7609 // are looked at before the extension object.
7610 Handle<JSFunction> go_between =
7611 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7612 go_between->set_context(function->context());
7613#ifdef DEBUG
7614 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7615 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7616 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7617#endif
7618
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007619 // Materialize the content of the local scope into a JSObject.
7620 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007621
7622 // Allocate a new context for the debug evaluation and set the extension
7623 // object build.
7624 Handle<Context> context =
7625 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007626 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007627 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007628 Handle<Context> frame_context(Context::cast(frame->context()));
7629 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007630 context = CopyWithContextChain(frame_context, context);
7631
7632 // Wrap the evaluation statement in a new function compiled in the newly
7633 // created context. The function has one parameter which has to be called
7634 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007635 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007636 // function(arguments,__source__) {return eval(__source__);}
7637 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007638 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007639 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007640 Handle<String> function_source =
7641 Factory::NewStringFromAscii(Vector<const char>(source_str,
7642 source_str_length));
7643 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007644 Compiler::CompileEval(function_source,
7645 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007646 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007647 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007648 if (boilerplate.is_null()) return Failure::Exception();
7649 Handle<JSFunction> compiled_function =
7650 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7651
7652 // Invoke the result of the compilation to get the evaluation function.
7653 bool has_pending_exception;
7654 Handle<Object> receiver(frame->receiver());
7655 Handle<Object> evaluation_function =
7656 Execution::Call(compiled_function, receiver, 0, NULL,
7657 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007658 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007659
7660 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7661 function_context);
7662
7663 // Invoke the evaluation function and return the result.
7664 const int argc = 2;
7665 Object** argv[argc] = { arguments.location(),
7666 Handle<Object>::cast(source).location() };
7667 Handle<Object> result =
7668 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7669 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007670 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007671
7672 // Skip the global proxy as it has no properties and always delegates to the
7673 // real global object.
7674 if (result->IsJSGlobalProxy()) {
7675 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7676 }
7677
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007678 return *result;
7679}
7680
7681
7682static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7683 HandleScope scope;
7684
7685 // Check the execution state and decode arguments frame and source to be
7686 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007687 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007688 Object* check_result = Runtime_CheckExecutionState(args);
7689 if (check_result->IsFailure()) return check_result;
7690 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007691 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7692
7693 // Handle the processing of break.
7694 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007695
7696 // Enter the top context from before the debugger was invoked.
7697 SaveContext save;
7698 SaveContext* top = &save;
7699 while (top != NULL && *top->context() == *Debug::debug_context()) {
7700 top = top->prev();
7701 }
7702 if (top != NULL) {
7703 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007704 }
7705
7706 // Get the global context now set to the top context from before the
7707 // debugger was invoked.
7708 Handle<Context> context = Top::global_context();
7709
7710 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007711 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007712 Handle<JSFunction>(Compiler::CompileEval(source,
7713 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007714 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007715 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007716 if (boilerplate.is_null()) return Failure::Exception();
7717 Handle<JSFunction> compiled_function =
7718 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7719 context));
7720
7721 // Invoke the result of the compilation to get the evaluation function.
7722 bool has_pending_exception;
7723 Handle<Object> receiver = Top::global();
7724 Handle<Object> result =
7725 Execution::Call(compiled_function, receiver, 0, NULL,
7726 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007727 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007728 return *result;
7729}
7730
7731
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007732static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7733 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007734 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007736 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007737 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007738
7739 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007740 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007741 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7742 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7743 // because using
7744 // instances->set(i, *GetScriptWrapper(script))
7745 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7746 // already have deferenced the instances handle.
7747 Handle<JSValue> wrapper = GetScriptWrapper(script);
7748 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007749 }
7750
7751 // Return result as a JS array.
7752 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7753 Handle<JSArray>::cast(result)->SetContent(*instances);
7754 return *result;
7755}
7756
7757
7758// Helper function used by Runtime_DebugReferencedBy below.
7759static int DebugReferencedBy(JSObject* target,
7760 Object* instance_filter, int max_references,
7761 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007762 JSFunction* arguments_function) {
7763 NoHandleAllocation ha;
7764 AssertNoAllocation no_alloc;
7765
7766 // Iterate the heap.
7767 int count = 0;
7768 JSObject* last = NULL;
7769 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007770 HeapObject* heap_obj = NULL;
7771 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007772 (max_references == 0 || count < max_references)) {
7773 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007774 if (heap_obj->IsJSObject()) {
7775 // Skip context extension objects and argument arrays as these are
7776 // checked in the context of functions using them.
7777 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007778 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007779 obj->map()->constructor() == arguments_function) {
7780 continue;
7781 }
7782
7783 // Check if the JS object has a reference to the object looked for.
7784 if (obj->ReferencesObject(target)) {
7785 // Check instance filter if supplied. This is normally used to avoid
7786 // references from mirror objects (see Runtime_IsInPrototypeChain).
7787 if (!instance_filter->IsUndefined()) {
7788 Object* V = obj;
7789 while (true) {
7790 Object* prototype = V->GetPrototype();
7791 if (prototype->IsNull()) {
7792 break;
7793 }
7794 if (instance_filter == prototype) {
7795 obj = NULL; // Don't add this object.
7796 break;
7797 }
7798 V = prototype;
7799 }
7800 }
7801
7802 if (obj != NULL) {
7803 // Valid reference found add to instance array if supplied an update
7804 // count.
7805 if (instances != NULL && count < instances_size) {
7806 instances->set(count, obj);
7807 }
7808 last = obj;
7809 count++;
7810 }
7811 }
7812 }
7813 }
7814
7815 // Check for circular reference only. This can happen when the object is only
7816 // referenced from mirrors and has a circular reference in which case the
7817 // object is not really alive and would have been garbage collected if not
7818 // referenced from the mirror.
7819 if (count == 1 && last == target) {
7820 count = 0;
7821 }
7822
7823 // Return the number of referencing objects found.
7824 return count;
7825}
7826
7827
7828// Scan the heap for objects with direct references to an object
7829// args[0]: the object to find references to
7830// args[1]: constructor function for instances to exclude (Mirror)
7831// args[2]: the the maximum number of objects to return
7832static Object* Runtime_DebugReferencedBy(Arguments args) {
7833 ASSERT(args.length() == 3);
7834
7835 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007836 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007837
7838 // Check parameters.
7839 CONVERT_CHECKED(JSObject, target, args[0]);
7840 Object* instance_filter = args[1];
7841 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7842 instance_filter->IsJSObject());
7843 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7844 RUNTIME_ASSERT(max_references >= 0);
7845
7846 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007847 JSObject* arguments_boilerplate =
7848 Top::context()->global_context()->arguments_boilerplate();
7849 JSFunction* arguments_function =
7850 JSFunction::cast(arguments_boilerplate->map()->constructor());
7851
7852 // Get the number of referencing objects.
7853 int count;
7854 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007855 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007856
7857 // Allocate an array to hold the result.
7858 Object* object = Heap::AllocateFixedArray(count);
7859 if (object->IsFailure()) return object;
7860 FixedArray* instances = FixedArray::cast(object);
7861
7862 // Fill the referencing objects.
7863 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007864 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007865
7866 // Return result as JS array.
7867 Object* result =
7868 Heap::AllocateJSObject(
7869 Top::context()->global_context()->array_function());
7870 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7871 return result;
7872}
7873
7874
7875// Helper function used by Runtime_DebugConstructedBy below.
7876static int DebugConstructedBy(JSFunction* constructor, int max_references,
7877 FixedArray* instances, int instances_size) {
7878 AssertNoAllocation no_alloc;
7879
7880 // Iterate the heap.
7881 int count = 0;
7882 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007883 HeapObject* heap_obj = NULL;
7884 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007885 (max_references == 0 || count < max_references)) {
7886 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007887 if (heap_obj->IsJSObject()) {
7888 JSObject* obj = JSObject::cast(heap_obj);
7889 if (obj->map()->constructor() == constructor) {
7890 // Valid reference found add to instance array if supplied an update
7891 // count.
7892 if (instances != NULL && count < instances_size) {
7893 instances->set(count, obj);
7894 }
7895 count++;
7896 }
7897 }
7898 }
7899
7900 // Return the number of referencing objects found.
7901 return count;
7902}
7903
7904
7905// Scan the heap for objects constructed by a specific function.
7906// args[0]: the constructor to find instances of
7907// args[1]: the the maximum number of objects to return
7908static Object* Runtime_DebugConstructedBy(Arguments args) {
7909 ASSERT(args.length() == 2);
7910
7911 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007912 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007913
7914 // Check parameters.
7915 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7916 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7917 RUNTIME_ASSERT(max_references >= 0);
7918
7919 // Get the number of referencing objects.
7920 int count;
7921 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7922
7923 // Allocate an array to hold the result.
7924 Object* object = Heap::AllocateFixedArray(count);
7925 if (object->IsFailure()) return object;
7926 FixedArray* instances = FixedArray::cast(object);
7927
7928 // Fill the referencing objects.
7929 count = DebugConstructedBy(constructor, max_references, instances, count);
7930
7931 // Return result as JS array.
7932 Object* result =
7933 Heap::AllocateJSObject(
7934 Top::context()->global_context()->array_function());
7935 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7936 return result;
7937}
7938
7939
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007940// Find the effective prototype object as returned by __proto__.
7941// args[0]: the object to find the prototype for.
7942static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007943 ASSERT(args.length() == 1);
7944
7945 CONVERT_CHECKED(JSObject, obj, args[0]);
7946
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007947 // Use the __proto__ accessor.
7948 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007949}
7950
7951
7952static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007953 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007954 CPU::DebugBreak();
7955 return Heap::undefined_value();
7956}
7957
7958
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007959static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007960#ifdef DEBUG
7961 HandleScope scope;
7962 ASSERT(args.length() == 1);
7963 // Get the function and make sure it is compiled.
7964 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007965 Handle<SharedFunctionInfo> shared(func->shared());
7966 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007967 return Failure::Exception();
7968 }
7969 func->code()->PrintLn();
7970#endif // DEBUG
7971 return Heap::undefined_value();
7972}
ager@chromium.org9085a012009-05-11 19:22:57 +00007973
7974
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007975static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7976#ifdef DEBUG
7977 HandleScope scope;
7978 ASSERT(args.length() == 1);
7979 // Get the function and make sure it is compiled.
7980 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007981 Handle<SharedFunctionInfo> shared(func->shared());
7982 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007983 return Failure::Exception();
7984 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007985 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007986#endif // DEBUG
7987 return Heap::undefined_value();
7988}
7989
7990
ager@chromium.org9085a012009-05-11 19:22:57 +00007991static Object* Runtime_FunctionGetInferredName(Arguments args) {
7992 NoHandleAllocation ha;
7993 ASSERT(args.length() == 1);
7994
7995 CONVERT_CHECKED(JSFunction, f, args[0]);
7996 return f->shared()->inferred_name();
7997}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007998
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007999#endif // ENABLE_DEBUGGER_SUPPORT
8000
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008001#ifdef ENABLE_LOGGING_AND_PROFILING
8002
8003static Object* Runtime_ProfilerResume(Arguments args) {
8004 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00008005 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008006
8007 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00008008 CONVERT_CHECKED(Smi, smi_tag, args[1]);
8009 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008010 return Heap::undefined_value();
8011}
8012
8013
8014static Object* Runtime_ProfilerPause(Arguments args) {
8015 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00008016 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008017
8018 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00008019 CONVERT_CHECKED(Smi, smi_tag, args[1]);
8020 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008021 return Heap::undefined_value();
8022}
8023
8024#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008025
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008026// Finds the script object from the script data. NOTE: This operation uses
8027// heap traversal to find the function generated for the source position
8028// for the requested break point. For lazily compiled functions several heap
8029// traversals might be required rendering this operation as a rather slow
8030// operation. However for setting break points which is normally done through
8031// some kind of user interaction the performance is not crucial.
8032static Handle<Object> Runtime_GetScriptFromScriptName(
8033 Handle<String> script_name) {
8034 // Scan the heap for Script objects to find the script with the requested
8035 // script data.
8036 Handle<Script> script;
8037 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008038 HeapObject* obj = NULL;
8039 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008040 // If a script is found check if it has the script data requested.
8041 if (obj->IsScript()) {
8042 if (Script::cast(obj)->name()->IsString()) {
8043 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
8044 script = Handle<Script>(Script::cast(obj));
8045 }
8046 }
8047 }
8048 }
8049
8050 // If no script with the requested script data is found return undefined.
8051 if (script.is_null()) return Factory::undefined_value();
8052
8053 // Return the script found.
8054 return GetScriptWrapper(script);
8055}
8056
8057
8058// Get the script object from script data. NOTE: Regarding performance
8059// see the NOTE for GetScriptFromScriptData.
8060// args[0]: script data for the script to find the source for
8061static Object* Runtime_GetScript(Arguments args) {
8062 HandleScope scope;
8063
8064 ASSERT(args.length() == 1);
8065
8066 CONVERT_CHECKED(String, script_name, args[0]);
8067
8068 // Find the requested script.
8069 Handle<Object> result =
8070 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
8071 return *result;
8072}
8073
8074
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008075// Determines whether the given stack frame should be displayed in
8076// a stack trace. The caller is the error constructor that asked
8077// for the stack trace to be collected. The first time a construct
8078// call to this function is encountered it is skipped. The seen_caller
8079// in/out parameter is used to remember if the caller has been seen
8080// yet.
8081static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
8082 bool* seen_caller) {
8083 // Only display JS frames.
8084 if (!raw_frame->is_java_script())
8085 return false;
8086 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
8087 Object* raw_fun = frame->function();
8088 // Not sure when this can happen but skip it just in case.
8089 if (!raw_fun->IsJSFunction())
8090 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008091 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008092 *seen_caller = true;
8093 return false;
8094 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008095 // Skip all frames until we've seen the caller. Also, skip the most
8096 // obvious builtin calls. Some builtin calls (such as Number.ADD
8097 // which is invoked using 'call') are very difficult to recognize
8098 // so we're leaving them in for now.
8099 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008100}
8101
8102
8103// Collect the raw data for a stack trace. Returns an array of three
8104// element segments each containing a receiver, function and native
8105// code offset.
8106static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008107 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008108 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008109 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
8110
8111 HandleScope scope;
8112
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008113 limit = Max(limit, 0); // Ensure that limit is not negative.
8114 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008115 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008116
8117 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008118 // If the caller parameter is a function we skip frames until we're
8119 // under it before starting to collect.
8120 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008121 int cursor = 0;
8122 int frames_seen = 0;
8123 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008124 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008125 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008126 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008127 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008128 Object* recv = frame->receiver();
8129 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008130 Address pc = frame->pc();
8131 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008132 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008133 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008134 if (cursor + 2 < elements->length()) {
8135 elements->set(cursor++, recv);
8136 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008137 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008138 } else {
8139 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008140 Handle<Object> recv_handle(recv);
8141 Handle<Object> fun_handle(fun);
8142 SetElement(result, cursor++, recv_handle);
8143 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008144 SetElement(result, cursor++, Handle<Smi>(offset));
8145 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008146 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008147 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008148 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008149
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008150 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008151 return *result;
8152}
8153
8154
ager@chromium.org3811b432009-10-28 14:53:37 +00008155// Returns V8 version as a string.
8156static Object* Runtime_GetV8Version(Arguments args) {
8157 ASSERT_EQ(args.length(), 0);
8158
8159 NoHandleAllocation ha;
8160
8161 const char* version_string = v8::V8::GetVersion();
8162
8163 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
8164}
8165
8166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008167static Object* Runtime_Abort(Arguments args) {
8168 ASSERT(args.length() == 2);
8169 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
8170 Smi::cast(args[1])->value());
8171 Top::PrintStack();
8172 OS::Abort();
8173 UNREACHABLE();
8174 return NULL;
8175}
8176
8177
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008178static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
8179 ASSERT(args.length() == 0);
8180 HandleScope::DeleteExtensions();
8181 return Heap::undefined_value();
8182}
8183
8184
kasper.lund44510672008-07-25 07:37:58 +00008185#ifdef DEBUG
8186// ListNatives is ONLY used by the fuzz-natives.js in debug mode
8187// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008188static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008189 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008190 HandleScope scope;
8191 Handle<JSArray> result = Factory::NewJSArray(0);
8192 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00008193#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008194 { \
8195 HandleScope inner; \
8196 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008197 Factory::NewStringFromAscii( \
8198 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008199 Handle<JSArray> pair = Factory::NewJSArray(0); \
8200 SetElement(pair, 0, name); \
8201 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
8202 SetElement(result, index++, pair); \
8203 }
8204 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
8205#undef ADD_ENTRY
8206 return *result;
8207}
kasper.lund44510672008-07-25 07:37:58 +00008208#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008209
8210
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008211static Object* Runtime_Log(Arguments args) {
8212 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00008213 CONVERT_CHECKED(String, format, args[0]);
8214 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008215 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008216 Logger::LogRuntime(chars, elms);
8217 return Heap::undefined_value();
8218}
8219
8220
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008221static Object* Runtime_IS_VAR(Arguments args) {
8222 UNREACHABLE(); // implemented as macro in the parser
8223 return NULL;
8224}
8225
8226
8227// ----------------------------------------------------------------------------
8228// Implementation of Runtime
8229
ager@chromium.orga1645e22009-09-09 19:27:10 +00008230#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008231 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00008232 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008233
8234static Runtime::Function Runtime_functions[] = {
8235 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008236 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008237};
8238
8239#undef F
8240
8241
8242Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
8243 ASSERT(0 <= fid && fid < kNofFunctions);
8244 return &Runtime_functions[fid];
8245}
8246
8247
8248Runtime::Function* Runtime::FunctionForName(const char* name) {
8249 for (Function* f = Runtime_functions; f->name != NULL; f++) {
8250 if (strcmp(f->name, name) == 0) {
8251 return f;
8252 }
8253 }
8254 return NULL;
8255}
8256
8257
8258void Runtime::PerformGC(Object* result) {
8259 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008260 if (failure->IsRetryAfterGC()) {
8261 // Try to do a garbage collection; ignore it if it fails. The C
8262 // entry stub will throw an out-of-memory exception in that case.
8263 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
8264 } else {
8265 // Handle last resort GC and make sure to allow future allocations
8266 // to grow the heap without causing GCs (if possible).
8267 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008268 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008269 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008270}
8271
8272
8273} } // namespace v8::internal