blob: b1d6024e0ae7eb9544ddcf1dec1f2f11c32e51b4 [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
2279// Trivial string search for shorter strings.
2280// On return, if "complete" is set to true, the return value is the
2281// final result of searching for the patter in the subject.
2282// If "complete" is set to false, the return value is the index where
2283// further checking should start, i.e., it's guaranteed that the pattern
2284// does not occur at a position prior to the returned index.
2285template <typename pchar, typename schar>
2286static int SimpleIndexOf(Vector<const schar> subject,
2287 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002288 int idx,
2289 bool* complete) {
2290 // Badness is a count of how much work we have done. When we have
2291 // done enough work we decide it's probably worth switching to a better
2292 // algorithm.
2293 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002294 // We know our pattern is at least 2 characters, we cache the first so
2295 // the common case of the first character not matching is faster.
2296 pchar pattern_first_char = pattern[0];
2297
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002298 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2299 badness++;
2300 if (badness > 0) {
2301 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002302 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002303 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002304 if (subject[i] != pattern_first_char) continue;
2305 int j = 1;
2306 do {
2307 if (pattern[j] != subject[i+j]) {
2308 break;
2309 }
2310 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002311 } while (j < pattern.length());
2312 if (j == pattern.length()) {
2313 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002314 return i;
2315 }
2316 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002317 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002318 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002319 return -1;
2320}
2321
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002322// Simple indexOf that never bails out. For short patterns only.
2323template <typename pchar, typename schar>
2324static int SimpleIndexOf(Vector<const schar> subject,
2325 Vector<const pchar> pattern,
2326 int idx) {
2327 pchar pattern_first_char = pattern[0];
2328 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2329 if (subject[i] != pattern_first_char) continue;
2330 int j = 1;
2331 do {
2332 if (pattern[j] != subject[i+j]) {
2333 break;
2334 }
2335 j++;
2336 } while (j < pattern.length());
2337 if (j == pattern.length()) {
2338 return i;
2339 }
2340 }
2341 return -1;
2342}
2343
2344
2345// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002346template <typename schar, typename pchar>
2347static int StringMatchStrategy(Vector<const schar> sub,
2348 Vector<const pchar> pat,
2349 int start_index) {
2350 ASSERT(pat.length() > 1);
2351
2352 // We have an ASCII haystack and a non-ASCII needle. Check if there
2353 // really is a non-ASCII character in the needle and bail out if there
2354 // is.
2355 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2356 for (int i = 0; i < pat.length(); i++) {
2357 uc16 c = pat[i];
2358 if (c > String::kMaxAsciiCharCode) {
2359 return -1;
2360 }
2361 }
2362 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002363 if (pat.length() < kBMMinPatternLength) {
2364 // We don't believe fancy searching can ever be more efficient.
2365 // The max shift of Boyer-Moore on a pattern of this length does
2366 // not compensate for the overhead.
2367 return SimpleIndexOf(sub, pat, start_index);
2368 }
2369 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002370 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002371 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2372 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002373 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002374 if (complete) return idx;
2375 return BoyerMooreIndexOf(sub, pat, idx);
2376}
2377
2378// Perform string match of pattern on subject, starting at start index.
2379// Caller must ensure that 0 <= start_index <= sub->length(),
2380// and should check that pat->length() + start_index <= sub->length()
2381int Runtime::StringMatch(Handle<String> sub,
2382 Handle<String> pat,
2383 int start_index) {
2384 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002385 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002386
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002387 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002388 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002389
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002390 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002391 if (start_index + pattern_length > subject_length) return -1;
2392
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002393 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002394 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002395 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002396 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002397 // character patterns linear search is necessary, so any smart
2398 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002399 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002400 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002401 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002402 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002403 if (pchar > String::kMaxAsciiCharCode) {
2404 return -1;
2405 }
2406 Vector<const char> ascii_vector =
2407 sub->ToAsciiVector().SubVector(start_index, subject_length);
2408 const void* pos = memchr(ascii_vector.start(),
2409 static_cast<const char>(pchar),
2410 static_cast<size_t>(ascii_vector.length()));
2411 if (pos == NULL) {
2412 return -1;
2413 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002414 return static_cast<int>(reinterpret_cast<const char*>(pos)
2415 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002416 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002417 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002418 }
2419
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002420 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002421 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002422 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002423
ager@chromium.org7c537e22008-10-16 08:43:32 +00002424 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2425 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002426 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002427 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002428 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002429 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002430 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002431 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002432 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002433 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002434 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002435 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002436 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002437 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002438}
2439
2440
2441static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002442 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002443 ASSERT(args.length() == 3);
2444
ager@chromium.org7c537e22008-10-16 08:43:32 +00002445 CONVERT_ARG_CHECKED(String, sub, 0);
2446 CONVERT_ARG_CHECKED(String, pat, 1);
2447
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002448 Object* index = args[2];
2449 uint32_t start_index;
2450 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2451
ager@chromium.org870a0b62008-11-04 11:43:05 +00002452 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002453 int position = Runtime::StringMatch(sub, pat, start_index);
2454 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002455}
2456
2457
2458static Object* Runtime_StringLastIndexOf(Arguments args) {
2459 NoHandleAllocation ha;
2460 ASSERT(args.length() == 3);
2461
2462 CONVERT_CHECKED(String, sub, args[0]);
2463 CONVERT_CHECKED(String, pat, args[1]);
2464 Object* index = args[2];
2465
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002466 sub->TryFlattenIfNotFlat();
2467 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002468
2469 uint32_t start_index;
2470 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2471
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002472 uint32_t pattern_length = pat->length();
2473 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002475 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002476 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002477 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002478
2479 for (int i = start_index; i >= 0; i--) {
2480 bool found = true;
2481 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002482 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002483 found = false;
2484 break;
2485 }
2486 }
2487 if (found) return Smi::FromInt(i);
2488 }
2489
2490 return Smi::FromInt(-1);
2491}
2492
2493
2494static Object* Runtime_StringLocaleCompare(Arguments args) {
2495 NoHandleAllocation ha;
2496 ASSERT(args.length() == 2);
2497
2498 CONVERT_CHECKED(String, str1, args[0]);
2499 CONVERT_CHECKED(String, str2, args[1]);
2500
2501 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002502 int str1_length = str1->length();
2503 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002504
2505 // Decide trivial cases without flattening.
2506 if (str1_length == 0) {
2507 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2508 return Smi::FromInt(-str2_length);
2509 } else {
2510 if (str2_length == 0) return Smi::FromInt(str1_length);
2511 }
2512
2513 int end = str1_length < str2_length ? str1_length : str2_length;
2514
2515 // No need to flatten if we are going to find the answer on the first
2516 // character. At this point we know there is at least one character
2517 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002518 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002519 if (d != 0) return Smi::FromInt(d);
2520
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002521 str1->TryFlattenIfNotFlat();
2522 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002523
2524 static StringInputBuffer buf1;
2525 static StringInputBuffer buf2;
2526
2527 buf1.Reset(str1);
2528 buf2.Reset(str2);
2529
2530 for (int i = 0; i < end; i++) {
2531 uint16_t char1 = buf1.GetNext();
2532 uint16_t char2 = buf2.GetNext();
2533 if (char1 != char2) return Smi::FromInt(char1 - char2);
2534 }
2535
2536 return Smi::FromInt(str1_length - str2_length);
2537}
2538
2539
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002540static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002541 NoHandleAllocation ha;
2542 ASSERT(args.length() == 3);
2543
2544 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002545 Object* from = args[1];
2546 Object* to = args[2];
2547 int start, end;
2548 // We have a fast integer-only case here to avoid a conversion to double in
2549 // the common case where from and to are Smis.
2550 if (from->IsSmi() && to->IsSmi()) {
2551 start = Smi::cast(from)->value();
2552 end = Smi::cast(to)->value();
2553 } else {
2554 CONVERT_DOUBLE_CHECKED(from_number, from);
2555 CONVERT_DOUBLE_CHECKED(to_number, to);
2556 start = FastD2I(from_number);
2557 end = FastD2I(to_number);
2558 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002559 RUNTIME_ASSERT(end >= start);
2560 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002561 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002562 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002563 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002564}
2565
2566
ager@chromium.org41826e72009-03-30 13:30:57 +00002567static Object* Runtime_StringMatch(Arguments args) {
2568 ASSERT_EQ(3, args.length());
2569
2570 CONVERT_ARG_CHECKED(String, subject, 0);
2571 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2572 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2573 HandleScope handles;
2574
2575 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2576
2577 if (match.is_null()) {
2578 return Failure::Exception();
2579 }
2580 if (match->IsNull()) {
2581 return Heap::null_value();
2582 }
2583 int length = subject->length();
2584
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002585 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002586 ZoneList<int> offsets(8);
2587 do {
2588 int start;
2589 int end;
2590 {
2591 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002592 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002593 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2594 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2595 }
2596 offsets.Add(start);
2597 offsets.Add(end);
2598 int index = start < end ? end : end + 1;
2599 if (index > length) break;
2600 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2601 if (match.is_null()) {
2602 return Failure::Exception();
2603 }
2604 } while (!match->IsNull());
2605 int matches = offsets.length() / 2;
2606 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2607 for (int i = 0; i < matches ; i++) {
2608 int from = offsets.at(i * 2);
2609 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002610 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002611 }
2612 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2613 result->set_length(Smi::FromInt(matches));
2614 return *result;
2615}
2616
2617
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002618static Object* Runtime_NumberToRadixString(Arguments args) {
2619 NoHandleAllocation ha;
2620 ASSERT(args.length() == 2);
2621
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002622 // Fast case where the result is a one character string.
2623 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2624 int value = Smi::cast(args[0])->value();
2625 int radix = Smi::cast(args[1])->value();
2626 if (value >= 0 && value < radix) {
2627 RUNTIME_ASSERT(radix <= 36);
2628 // Character array used for conversion.
2629 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2630 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2631 }
2632 }
2633
2634 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002635 CONVERT_DOUBLE_CHECKED(value, args[0]);
2636 if (isnan(value)) {
2637 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2638 }
2639 if (isinf(value)) {
2640 if (value < 0) {
2641 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2642 }
2643 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2644 }
2645 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2646 int radix = FastD2I(radix_number);
2647 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2648 char* str = DoubleToRadixCString(value, radix);
2649 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2650 DeleteArray(str);
2651 return result;
2652}
2653
2654
2655static Object* Runtime_NumberToFixed(Arguments args) {
2656 NoHandleAllocation ha;
2657 ASSERT(args.length() == 2);
2658
2659 CONVERT_DOUBLE_CHECKED(value, args[0]);
2660 if (isnan(value)) {
2661 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2662 }
2663 if (isinf(value)) {
2664 if (value < 0) {
2665 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2666 }
2667 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2668 }
2669 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2670 int f = FastD2I(f_number);
2671 RUNTIME_ASSERT(f >= 0);
2672 char* str = DoubleToFixedCString(value, f);
2673 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2674 DeleteArray(str);
2675 return res;
2676}
2677
2678
2679static Object* Runtime_NumberToExponential(Arguments args) {
2680 NoHandleAllocation ha;
2681 ASSERT(args.length() == 2);
2682
2683 CONVERT_DOUBLE_CHECKED(value, args[0]);
2684 if (isnan(value)) {
2685 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2686 }
2687 if (isinf(value)) {
2688 if (value < 0) {
2689 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2690 }
2691 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2692 }
2693 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2694 int f = FastD2I(f_number);
2695 RUNTIME_ASSERT(f >= -1 && f <= 20);
2696 char* str = DoubleToExponentialCString(value, f);
2697 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2698 DeleteArray(str);
2699 return res;
2700}
2701
2702
2703static Object* Runtime_NumberToPrecision(Arguments args) {
2704 NoHandleAllocation ha;
2705 ASSERT(args.length() == 2);
2706
2707 CONVERT_DOUBLE_CHECKED(value, args[0]);
2708 if (isnan(value)) {
2709 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2710 }
2711 if (isinf(value)) {
2712 if (value < 0) {
2713 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2714 }
2715 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2716 }
2717 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2718 int f = FastD2I(f_number);
2719 RUNTIME_ASSERT(f >= 1 && f <= 21);
2720 char* str = DoubleToPrecisionCString(value, f);
2721 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2722 DeleteArray(str);
2723 return res;
2724}
2725
2726
2727// Returns a single character string where first character equals
2728// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002729static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002730 if (index < static_cast<uint32_t>(string->length())) {
2731 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002732 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002733 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002734 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002735 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002736}
2737
2738
2739Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2740 // Handle [] indexing on Strings
2741 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002742 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2743 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002744 }
2745
2746 // Handle [] indexing on String objects
2747 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002748 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2749 Handle<Object> result =
2750 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2751 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002752 }
2753
2754 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002755 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002756 return prototype->GetElement(index);
2757 }
2758
2759 return object->GetElement(index);
2760}
2761
2762
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002763Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2764 HandleScope scope;
2765
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002766 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002767 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002768 Handle<Object> error =
2769 Factory::NewTypeError("non_object_property_load",
2770 HandleVector(args, 2));
2771 return Top::Throw(*error);
2772 }
2773
2774 // Check if the given key is an array index.
2775 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002776 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002777 return GetElementOrCharAt(object, index);
2778 }
2779
2780 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002781 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002782 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002783 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002784 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002785 bool has_pending_exception = false;
2786 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002787 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002788 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002789 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002790 }
2791
ager@chromium.org32912102009-01-16 10:38:43 +00002792 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002793 // the element if so.
2794 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002795 return GetElementOrCharAt(object, index);
2796 } else {
2797 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002798 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002799 }
2800}
2801
2802
2803static Object* Runtime_GetProperty(Arguments args) {
2804 NoHandleAllocation ha;
2805 ASSERT(args.length() == 2);
2806
2807 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002808 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002809
2810 return Runtime::GetObjectProperty(object, key);
2811}
2812
2813
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002814// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002815static Object* Runtime_KeyedGetProperty(Arguments args) {
2816 NoHandleAllocation ha;
2817 ASSERT(args.length() == 2);
2818
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002819 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002820 // itself.
2821 //
2822 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002823 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002824 // global proxy object never has properties. This is the case
2825 // because the global proxy object forwards everything to its hidden
2826 // prototype including local lookups.
2827 //
2828 // Additionally, we need to make sure that we do not cache results
2829 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002830 if (args[0]->IsJSObject() &&
2831 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002832 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002833 args[1]->IsString()) {
2834 JSObject* receiver = JSObject::cast(args[0]);
2835 String* key = String::cast(args[1]);
2836 if (receiver->HasFastProperties()) {
2837 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002838 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002839 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2840 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002841 Object* value = receiver->FastPropertyAt(offset);
2842 return value->IsTheHole() ? Heap::undefined_value() : value;
2843 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002844 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002845 LookupResult result;
2846 receiver->LocalLookup(key, &result);
2847 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2848 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002849 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002850 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002851 }
2852 } else {
2853 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002854 StringDictionary* dictionary = receiver->property_dictionary();
2855 int entry = dictionary->FindEntry(key);
2856 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002857 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002858 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002859 if (!receiver->IsGlobalObject()) return value;
2860 value = JSGlobalPropertyCell::cast(value)->value();
2861 if (!value->IsTheHole()) return value;
2862 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002863 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002864 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002865 } else if (args[0]->IsString() && args[1]->IsSmi()) {
2866 // Fast case for string indexing using [] with a smi index.
2867 HandleScope scope;
2868 Handle<String> str = args.at<String>(0);
2869 int index = Smi::cast(args[1])->value();
2870 Handle<Object> result = GetCharAt(str, index);
2871 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002872 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002873
2874 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002875 return Runtime::GetObjectProperty(args.at<Object>(0),
2876 args.at<Object>(1));
2877}
2878
2879
ager@chromium.org5c838252010-02-19 08:53:10 +00002880static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
2881 ASSERT(args.length() == 5);
2882 HandleScope scope;
2883 CONVERT_ARG_CHECKED(JSObject, obj, 0);
2884 CONVERT_CHECKED(String, name, args[1]);
2885 CONVERT_CHECKED(Smi, flag_setter, args[2]);
2886 CONVERT_CHECKED(JSFunction, fun, args[3]);
2887 CONVERT_CHECKED(Smi, flag_attr, args[4]);
2888 int unchecked = flag_attr->value();
2889 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2890 RUNTIME_ASSERT(!obj->IsNull());
2891 LookupResult result;
2892 obj->LocalLookupRealNamedProperty(name, &result);
2893
2894 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
2895 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
2896 // delete it to avoid running into trouble in DefineAccessor, which
2897 // handles this incorrectly if the property is readonly (does nothing)
2898 if (result.IsProperty() &&
2899 (result.type() == FIELD || result.type() == NORMAL
2900 || result.type() == CONSTANT_FUNCTION)) {
2901 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
2902 }
2903 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
2904}
2905
2906static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
2907 ASSERT(args.length() == 4);
2908 HandleScope scope;
2909 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
2910 CONVERT_ARG_CHECKED(String, name, 1);
2911 Handle<Object> obj_value = args.at<Object>(2);
2912
2913 CONVERT_CHECKED(Smi, flag, args[3]);
2914 int unchecked = flag->value();
2915 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2916
2917 LookupResult result;
2918 js_object->LocalLookupRealNamedProperty(*name, &result);
2919
2920 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
2921
2922 // Take special care when attributes are different and there is already
2923 // a property. For simplicity we normalize the property which enables us
2924 // to not worry about changing the instance_descriptor and creating a new
2925 // map. The current version of SetObjectProperty does not handle attributes
2926 // correctly in the case where a property is a field and is reset with
2927 // new attributes.
2928 if (result.IsProperty() && attr != result.GetAttributes()) {
2929 // New attributes - normalize to avoid writing to instance descriptor
2930 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
2931 // Use IgnoreAttributes version since a readonly property may be
2932 // overridden and SetProperty does not allow this.
2933 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
2934 *obj_value,
2935 attr);
2936 }
2937 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
2938}
2939
2940
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002941Object* Runtime::SetObjectProperty(Handle<Object> object,
2942 Handle<Object> key,
2943 Handle<Object> value,
2944 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002945 HandleScope scope;
2946
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002947 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002948 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002949 Handle<Object> error =
2950 Factory::NewTypeError("non_object_property_store",
2951 HandleVector(args, 2));
2952 return Top::Throw(*error);
2953 }
2954
2955 // If the object isn't a JavaScript object, we ignore the store.
2956 if (!object->IsJSObject()) return *value;
2957
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002958 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2959
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002960 // Check if the given key is an array index.
2961 uint32_t index;
2962 if (Array::IndexFromObject(*key, &index)) {
2963 ASSERT(attr == NONE);
2964
2965 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2966 // of a string using [] notation. We need to support this too in
2967 // JavaScript.
2968 // In the case of a String object we just need to redirect the assignment to
2969 // the underlying string if the index is in range. Since the underlying
2970 // string does nothing with the assignment then we can ignore such
2971 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002972 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002973 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002974 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002975
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002976 Handle<Object> result = SetElement(js_object, index, value);
2977 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002978 return *value;
2979 }
2980
2981 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002982 Handle<Object> result;
2983 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002984 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002985 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002986 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002987 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002988 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002989 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002990 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002991 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002992 return *value;
2993 }
2994
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002995 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002996 bool has_pending_exception = false;
2997 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2998 if (has_pending_exception) return Failure::Exception();
2999 Handle<String> name = Handle<String>::cast(converted);
3000
3001 if (name->AsArrayIndex(&index)) {
3002 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003003 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003004 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003005 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003006 }
3007}
3008
3009
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003010Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3011 Handle<Object> key,
3012 Handle<Object> value,
3013 PropertyAttributes attr) {
3014 HandleScope scope;
3015
3016 // Check if the given key is an array index.
3017 uint32_t index;
3018 if (Array::IndexFromObject(*key, &index)) {
3019 ASSERT(attr == NONE);
3020
3021 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3022 // of a string using [] notation. We need to support this too in
3023 // JavaScript.
3024 // In the case of a String object we just need to redirect the assignment to
3025 // the underlying string if the index is in range. Since the underlying
3026 // string does nothing with the assignment then we can ignore such
3027 // assignments.
3028 if (js_object->IsStringObjectWithCharacterAt(index)) {
3029 return *value;
3030 }
3031
3032 return js_object->SetElement(index, *value);
3033 }
3034
3035 if (key->IsString()) {
3036 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
3037 ASSERT(attr == NONE);
3038 return js_object->SetElement(index, *value);
3039 } else {
3040 Handle<String> key_string = Handle<String>::cast(key);
3041 key_string->TryFlattenIfNotFlat();
3042 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3043 *value,
3044 attr);
3045 }
3046 }
3047
3048 // Call-back into JavaScript to convert the key to a string.
3049 bool has_pending_exception = false;
3050 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3051 if (has_pending_exception) return Failure::Exception();
3052 Handle<String> name = Handle<String>::cast(converted);
3053
3054 if (name->AsArrayIndex(&index)) {
3055 ASSERT(attr == NONE);
3056 return js_object->SetElement(index, *value);
3057 } else {
3058 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3059 }
3060}
3061
3062
ager@chromium.orge2902be2009-06-08 12:21:35 +00003063Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3064 Handle<Object> key) {
3065 HandleScope scope;
3066
3067 // Check if the given key is an array index.
3068 uint32_t index;
3069 if (Array::IndexFromObject(*key, &index)) {
3070 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3071 // characters of a string using [] notation. In the case of a
3072 // String object we just need to redirect the deletion to the
3073 // underlying string if the index is in range. Since the
3074 // underlying string does nothing with the deletion, we can ignore
3075 // such deletions.
3076 if (js_object->IsStringObjectWithCharacterAt(index)) {
3077 return Heap::true_value();
3078 }
3079
3080 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3081 }
3082
3083 Handle<String> key_string;
3084 if (key->IsString()) {
3085 key_string = Handle<String>::cast(key);
3086 } else {
3087 // Call-back into JavaScript to convert the key to a string.
3088 bool has_pending_exception = false;
3089 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3090 if (has_pending_exception) return Failure::Exception();
3091 key_string = Handle<String>::cast(converted);
3092 }
3093
3094 key_string->TryFlattenIfNotFlat();
3095 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3096}
3097
3098
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003099static Object* Runtime_SetProperty(Arguments args) {
3100 NoHandleAllocation ha;
3101 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3102
3103 Handle<Object> object = args.at<Object>(0);
3104 Handle<Object> key = args.at<Object>(1);
3105 Handle<Object> value = args.at<Object>(2);
3106
3107 // Compute attributes.
3108 PropertyAttributes attributes = NONE;
3109 if (args.length() == 4) {
3110 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003111 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003112 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003113 RUNTIME_ASSERT(
3114 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3115 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003116 }
3117 return Runtime::SetObjectProperty(object, key, value, attributes);
3118}
3119
3120
3121// Set a local property, even if it is READ_ONLY. If the property does not
3122// exist, it will be added with attributes NONE.
3123static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
3124 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003125 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003126 CONVERT_CHECKED(JSObject, object, args[0]);
3127 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003128 // Compute attributes.
3129 PropertyAttributes attributes = NONE;
3130 if (args.length() == 4) {
3131 CONVERT_CHECKED(Smi, value_obj, args[3]);
3132 int unchecked_value = value_obj->value();
3133 // Only attribute bits should be set.
3134 RUNTIME_ASSERT(
3135 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3136 attributes = static_cast<PropertyAttributes>(unchecked_value);
3137 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003138
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003139 return object->
3140 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003141}
3142
3143
3144static Object* Runtime_DeleteProperty(Arguments args) {
3145 NoHandleAllocation ha;
3146 ASSERT(args.length() == 2);
3147
3148 CONVERT_CHECKED(JSObject, object, args[0]);
3149 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00003150 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003151}
3152
3153
ager@chromium.org9085a012009-05-11 19:22:57 +00003154static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
3155 Handle<String> key) {
3156 if (object->HasLocalProperty(*key)) return Heap::true_value();
3157 // Handle hidden prototypes. If there's a hidden prototype above this thing
3158 // then we have to check it for properties, because they are supposed to
3159 // look like they are on this object.
3160 Handle<Object> proto(object->GetPrototype());
3161 if (proto->IsJSObject() &&
3162 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
3163 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
3164 }
3165 return Heap::false_value();
3166}
3167
3168
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003169static Object* Runtime_HasLocalProperty(Arguments args) {
3170 NoHandleAllocation ha;
3171 ASSERT(args.length() == 2);
3172 CONVERT_CHECKED(String, key, args[1]);
3173
ager@chromium.org9085a012009-05-11 19:22:57 +00003174 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003175 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00003176 if (obj->IsJSObject()) {
3177 JSObject* object = JSObject::cast(obj);
3178 // Fast case - no interceptors.
3179 if (object->HasRealNamedProperty(key)) return Heap::true_value();
3180 // Slow case. Either it's not there or we have an interceptor. We should
3181 // have handles for this kind of deal.
3182 HandleScope scope;
3183 return HasLocalPropertyImplementation(Handle<JSObject>(object),
3184 Handle<String>(key));
3185 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003186 // Well, there is one exception: Handle [] on strings.
3187 uint32_t index;
3188 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00003189 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003190 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003191 return Heap::true_value();
3192 }
3193 }
3194 return Heap::false_value();
3195}
3196
3197
3198static Object* Runtime_HasProperty(Arguments args) {
3199 NoHandleAllocation na;
3200 ASSERT(args.length() == 2);
3201
3202 // Only JS objects can have properties.
3203 if (args[0]->IsJSObject()) {
3204 JSObject* object = JSObject::cast(args[0]);
3205 CONVERT_CHECKED(String, key, args[1]);
3206 if (object->HasProperty(key)) return Heap::true_value();
3207 }
3208 return Heap::false_value();
3209}
3210
3211
3212static Object* Runtime_HasElement(Arguments args) {
3213 NoHandleAllocation na;
3214 ASSERT(args.length() == 2);
3215
3216 // Only JS objects can have elements.
3217 if (args[0]->IsJSObject()) {
3218 JSObject* object = JSObject::cast(args[0]);
3219 CONVERT_CHECKED(Smi, index_obj, args[1]);
3220 uint32_t index = index_obj->value();
3221 if (object->HasElement(index)) return Heap::true_value();
3222 }
3223 return Heap::false_value();
3224}
3225
3226
3227static Object* Runtime_IsPropertyEnumerable(Arguments args) {
3228 NoHandleAllocation ha;
3229 ASSERT(args.length() == 2);
3230
3231 CONVERT_CHECKED(JSObject, object, args[0]);
3232 CONVERT_CHECKED(String, key, args[1]);
3233
3234 uint32_t index;
3235 if (key->AsArrayIndex(&index)) {
3236 return Heap::ToBoolean(object->HasElement(index));
3237 }
3238
ager@chromium.org870a0b62008-11-04 11:43:05 +00003239 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
3240 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241}
3242
3243
3244static Object* Runtime_GetPropertyNames(Arguments args) {
3245 HandleScope scope;
3246 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003247 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003248 return *GetKeysFor(object);
3249}
3250
3251
3252// Returns either a FixedArray as Runtime_GetPropertyNames,
3253// or, if the given object has an enum cache that contains
3254// all enumerable properties of the object and its prototypes
3255// have none, the map of the object. This is used to speed up
3256// the check for deletions during a for-in.
3257static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3258 ASSERT(args.length() == 1);
3259
3260 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3261
3262 if (raw_object->IsSimpleEnum()) return raw_object->map();
3263
3264 HandleScope scope;
3265 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003266 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3267 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003268
3269 // Test again, since cache may have been built by preceding call.
3270 if (object->IsSimpleEnum()) return object->map();
3271
3272 return *content;
3273}
3274
3275
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003276// Find the length of the prototype chain that is to to handled as one. If a
3277// prototype object is hidden it is to be viewed as part of the the object it
3278// is prototype for.
3279static int LocalPrototypeChainLength(JSObject* obj) {
3280 int count = 1;
3281 Object* proto = obj->GetPrototype();
3282 while (proto->IsJSObject() &&
3283 JSObject::cast(proto)->map()->is_hidden_prototype()) {
3284 count++;
3285 proto = JSObject::cast(proto)->GetPrototype();
3286 }
3287 return count;
3288}
3289
3290
3291// Return the names of the local named properties.
3292// args[0]: object
3293static Object* Runtime_GetLocalPropertyNames(Arguments args) {
3294 HandleScope scope;
3295 ASSERT(args.length() == 1);
3296 if (!args[0]->IsJSObject()) {
3297 return Heap::undefined_value();
3298 }
3299 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3300
3301 // Skip the global proxy as it has no properties and always delegates to the
3302 // real global object.
3303 if (obj->IsJSGlobalProxy()) {
3304 // Only collect names if access is permitted.
3305 if (obj->IsAccessCheckNeeded() &&
3306 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
3307 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
3308 return *Factory::NewJSArray(0);
3309 }
3310 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
3311 }
3312
3313 // Find the number of objects making up this.
3314 int length = LocalPrototypeChainLength(*obj);
3315
3316 // Find the number of local properties for each of the objects.
3317 int* local_property_count = NewArray<int>(length);
3318 int total_property_count = 0;
3319 Handle<JSObject> jsproto = obj;
3320 for (int i = 0; i < length; i++) {
3321 // Only collect names if access is permitted.
3322 if (jsproto->IsAccessCheckNeeded() &&
3323 !Top::MayNamedAccess(*jsproto,
3324 Heap::undefined_value(),
3325 v8::ACCESS_KEYS)) {
3326 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
3327 return *Factory::NewJSArray(0);
3328 }
3329 int n;
3330 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
3331 local_property_count[i] = n;
3332 total_property_count += n;
3333 if (i < length - 1) {
3334 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3335 }
3336 }
3337
3338 // Allocate an array with storage for all the property names.
3339 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
3340
3341 // Get the property names.
3342 jsproto = obj;
3343 int proto_with_hidden_properties = 0;
3344 for (int i = 0; i < length; i++) {
3345 jsproto->GetLocalPropertyNames(*names,
3346 i == 0 ? 0 : local_property_count[i - 1]);
3347 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
3348 proto_with_hidden_properties++;
3349 }
3350 if (i < length - 1) {
3351 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3352 }
3353 }
3354
3355 // Filter out name of hidden propeties object.
3356 if (proto_with_hidden_properties > 0) {
3357 Handle<FixedArray> old_names = names;
3358 names = Factory::NewFixedArray(
3359 names->length() - proto_with_hidden_properties);
3360 int dest_pos = 0;
3361 for (int i = 0; i < total_property_count; i++) {
3362 Object* name = old_names->get(i);
3363 if (name == Heap::hidden_symbol()) {
3364 continue;
3365 }
3366 names->set(dest_pos++, name);
3367 }
3368 }
3369
3370 DeleteArray(local_property_count);
3371 return *Factory::NewJSArrayWithElements(names);
3372}
3373
3374
3375// Return the names of the local indexed properties.
3376// args[0]: object
3377static Object* Runtime_GetLocalElementNames(Arguments args) {
3378 HandleScope scope;
3379 ASSERT(args.length() == 1);
3380 if (!args[0]->IsJSObject()) {
3381 return Heap::undefined_value();
3382 }
3383 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3384
3385 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
3386 Handle<FixedArray> names = Factory::NewFixedArray(n);
3387 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
3388 return *Factory::NewJSArrayWithElements(names);
3389}
3390
3391
3392// Return information on whether an object has a named or indexed interceptor.
3393// args[0]: object
3394static Object* Runtime_GetInterceptorInfo(Arguments args) {
3395 HandleScope scope;
3396 ASSERT(args.length() == 1);
3397 if (!args[0]->IsJSObject()) {
3398 return Smi::FromInt(0);
3399 }
3400 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3401
3402 int result = 0;
3403 if (obj->HasNamedInterceptor()) result |= 2;
3404 if (obj->HasIndexedInterceptor()) result |= 1;
3405
3406 return Smi::FromInt(result);
3407}
3408
3409
3410// Return property names from named interceptor.
3411// args[0]: object
3412static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
3413 HandleScope scope;
3414 ASSERT(args.length() == 1);
3415 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3416
3417 if (obj->HasNamedInterceptor()) {
3418 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
3419 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3420 }
3421 return Heap::undefined_value();
3422}
3423
3424
3425// Return element names from indexed interceptor.
3426// args[0]: object
3427static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
3428 HandleScope scope;
3429 ASSERT(args.length() == 1);
3430 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3431
3432 if (obj->HasIndexedInterceptor()) {
3433 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
3434 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3435 }
3436 return Heap::undefined_value();
3437}
3438
3439
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003440static Object* Runtime_LocalKeys(Arguments args) {
3441 ASSERT_EQ(args.length(), 1);
3442 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3443 HandleScope scope;
3444 Handle<JSObject> object(raw_object);
3445 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3446 LOCAL_ONLY);
3447 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3448 // property array and since the result is mutable we have to create
3449 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003450 int length = contents->length();
3451 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3452 for (int i = 0; i < length; i++) {
3453 Object* entry = contents->get(i);
3454 if (entry->IsString()) {
3455 copy->set(i, entry);
3456 } else {
3457 ASSERT(entry->IsNumber());
3458 HandleScope scope;
3459 Handle<Object> entry_handle(entry);
3460 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3461 copy->set(i, *entry_str);
3462 }
3463 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003464 return *Factory::NewJSArrayWithElements(copy);
3465}
3466
3467
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003468static Object* Runtime_GetArgumentsProperty(Arguments args) {
3469 NoHandleAllocation ha;
3470 ASSERT(args.length() == 1);
3471
3472 // Compute the frame holding the arguments.
3473 JavaScriptFrameIterator it;
3474 it.AdvanceToArgumentsFrame();
3475 JavaScriptFrame* frame = it.frame();
3476
3477 // Get the actual number of provided arguments.
3478 const uint32_t n = frame->GetProvidedParametersCount();
3479
3480 // Try to convert the key to an index. If successful and within
3481 // index return the the argument from the frame.
3482 uint32_t index;
3483 if (Array::IndexFromObject(args[0], &index) && index < n) {
3484 return frame->GetParameter(index);
3485 }
3486
3487 // Convert the key to a string.
3488 HandleScope scope;
3489 bool exception = false;
3490 Handle<Object> converted =
3491 Execution::ToString(args.at<Object>(0), &exception);
3492 if (exception) return Failure::Exception();
3493 Handle<String> key = Handle<String>::cast(converted);
3494
3495 // Try to convert the string key into an array index.
3496 if (key->AsArrayIndex(&index)) {
3497 if (index < n) {
3498 return frame->GetParameter(index);
3499 } else {
3500 return Top::initial_object_prototype()->GetElement(index);
3501 }
3502 }
3503
3504 // Handle special arguments properties.
3505 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3506 if (key->Equals(Heap::callee_symbol())) return frame->function();
3507
3508 // Lookup in the initial Object.prototype object.
3509 return Top::initial_object_prototype()->GetProperty(*key);
3510}
3511
3512
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003513static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003514 HandleScope scope;
3515
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003516 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003517 Handle<Object> object = args.at<Object>(0);
3518 if (object->IsJSObject()) {
3519 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00003520 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
3521 js_object->TransformToFastProperties(0);
3522 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003523 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003524 return *object;
3525}
3526
3527
3528static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003529 HandleScope scope;
3530
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003531 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003532 Handle<Object> object = args.at<Object>(0);
3533 if (object->IsJSObject()) {
3534 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003535 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003536 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003537 return *object;
3538}
3539
3540
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003541static Object* Runtime_ToBool(Arguments args) {
3542 NoHandleAllocation ha;
3543 ASSERT(args.length() == 1);
3544
3545 return args[0]->ToBoolean();
3546}
3547
3548
3549// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3550// Possible optimizations: put the type string into the oddballs.
3551static Object* Runtime_Typeof(Arguments args) {
3552 NoHandleAllocation ha;
3553
3554 Object* obj = args[0];
3555 if (obj->IsNumber()) return Heap::number_symbol();
3556 HeapObject* heap_obj = HeapObject::cast(obj);
3557
3558 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003559 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003560
3561 InstanceType instance_type = heap_obj->map()->instance_type();
3562 if (instance_type < FIRST_NONSTRING_TYPE) {
3563 return Heap::string_symbol();
3564 }
3565
3566 switch (instance_type) {
3567 case ODDBALL_TYPE:
3568 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3569 return Heap::boolean_symbol();
3570 }
3571 if (heap_obj->IsNull()) {
3572 return Heap::object_symbol();
3573 }
3574 ASSERT(heap_obj->IsUndefined());
3575 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003576 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003577 return Heap::function_symbol();
3578 default:
3579 // For any kind of object not handled above, the spec rule for
3580 // host objects gives that it is okay to return "object"
3581 return Heap::object_symbol();
3582 }
3583}
3584
3585
3586static Object* Runtime_StringToNumber(Arguments args) {
3587 NoHandleAllocation ha;
3588 ASSERT(args.length() == 1);
3589 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003590 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003591 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3592}
3593
3594
3595static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3596 NoHandleAllocation ha;
3597 ASSERT(args.length() == 1);
3598
3599 CONVERT_CHECKED(JSArray, codes, args[0]);
3600 int length = Smi::cast(codes->length())->value();
3601
3602 // Check if the string can be ASCII.
3603 int i;
3604 for (i = 0; i < length; i++) {
3605 Object* element = codes->GetElement(i);
3606 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3607 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3608 break;
3609 }
3610
3611 Object* object = NULL;
3612 if (i == length) { // The string is ASCII.
3613 object = Heap::AllocateRawAsciiString(length);
3614 } else { // The string is not ASCII.
3615 object = Heap::AllocateRawTwoByteString(length);
3616 }
3617
3618 if (object->IsFailure()) return object;
3619 String* result = String::cast(object);
3620 for (int i = 0; i < length; i++) {
3621 Object* element = codes->GetElement(i);
3622 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003623 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003624 }
3625 return result;
3626}
3627
3628
3629// kNotEscaped is generated by the following:
3630//
3631// #!/bin/perl
3632// for (my $i = 0; $i < 256; $i++) {
3633// print "\n" if $i % 16 == 0;
3634// my $c = chr($i);
3635// my $escaped = 1;
3636// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3637// print $escaped ? "0, " : "1, ";
3638// }
3639
3640
3641static bool IsNotEscaped(uint16_t character) {
3642 // Only for 8 bit characters, the rest are always escaped (in a different way)
3643 ASSERT(character < 256);
3644 static const char kNotEscaped[256] = {
3645 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3646 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3647 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3648 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3649 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3650 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3651 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3652 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3653 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3654 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3655 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3656 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3661 };
3662 return kNotEscaped[character] != 0;
3663}
3664
3665
3666static Object* Runtime_URIEscape(Arguments args) {
3667 const char hex_chars[] = "0123456789ABCDEF";
3668 NoHandleAllocation ha;
3669 ASSERT(args.length() == 1);
3670 CONVERT_CHECKED(String, source, args[0]);
3671
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003672 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003673
3674 int escaped_length = 0;
3675 int length = source->length();
3676 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003677 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003678 buffer->Reset(source);
3679 while (buffer->has_more()) {
3680 uint16_t character = buffer->GetNext();
3681 if (character >= 256) {
3682 escaped_length += 6;
3683 } else if (IsNotEscaped(character)) {
3684 escaped_length++;
3685 } else {
3686 escaped_length += 3;
3687 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003688 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003689 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003690 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003691 Top::context()->mark_out_of_memory();
3692 return Failure::OutOfMemoryException();
3693 }
3694 }
3695 }
3696 // No length change implies no change. Return original string if no change.
3697 if (escaped_length == length) {
3698 return source;
3699 }
3700 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3701 if (o->IsFailure()) return o;
3702 String* destination = String::cast(o);
3703 int dest_position = 0;
3704
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003705 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003706 buffer->Rewind();
3707 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003708 uint16_t chr = buffer->GetNext();
3709 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003710 destination->Set(dest_position, '%');
3711 destination->Set(dest_position+1, 'u');
3712 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3713 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3714 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3715 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003716 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003717 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003718 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719 dest_position++;
3720 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003721 destination->Set(dest_position, '%');
3722 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3723 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003724 dest_position += 3;
3725 }
3726 }
3727 return destination;
3728}
3729
3730
3731static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3732 static const signed char kHexValue['g'] = {
3733 -1, -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, -1, -1, -1, -1, -1,
3735 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3736 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3737 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3738 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3739 -1, 10, 11, 12, 13, 14, 15 };
3740
3741 if (character1 > 'f') return -1;
3742 int hi = kHexValue[character1];
3743 if (hi == -1) return -1;
3744 if (character2 > 'f') return -1;
3745 int lo = kHexValue[character2];
3746 if (lo == -1) return -1;
3747 return (hi << 4) + lo;
3748}
3749
3750
ager@chromium.org870a0b62008-11-04 11:43:05 +00003751static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003752 int i,
3753 int length,
3754 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003755 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003756 int32_t hi = 0;
3757 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003758 if (character == '%' &&
3759 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003760 source->Get(i + 1) == 'u' &&
3761 (hi = TwoDigitHex(source->Get(i + 2),
3762 source->Get(i + 3))) != -1 &&
3763 (lo = TwoDigitHex(source->Get(i + 4),
3764 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003765 *step = 6;
3766 return (hi << 8) + lo;
3767 } else if (character == '%' &&
3768 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003769 (lo = TwoDigitHex(source->Get(i + 1),
3770 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003771 *step = 3;
3772 return lo;
3773 } else {
3774 *step = 1;
3775 return character;
3776 }
3777}
3778
3779
3780static Object* Runtime_URIUnescape(Arguments args) {
3781 NoHandleAllocation ha;
3782 ASSERT(args.length() == 1);
3783 CONVERT_CHECKED(String, source, args[0]);
3784
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003785 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003786
3787 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003788 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003789
3790 int unescaped_length = 0;
3791 for (int i = 0; i < length; unescaped_length++) {
3792 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003793 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003794 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003795 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003796 i += step;
3797 }
3798
3799 // No length change implies no change. Return original string if no change.
3800 if (unescaped_length == length)
3801 return source;
3802
3803 Object* o = ascii ?
3804 Heap::AllocateRawAsciiString(unescaped_length) :
3805 Heap::AllocateRawTwoByteString(unescaped_length);
3806 if (o->IsFailure()) return o;
3807 String* destination = String::cast(o);
3808
3809 int dest_position = 0;
3810 for (int i = 0; i < length; dest_position++) {
3811 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003812 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003813 i += step;
3814 }
3815 return destination;
3816}
3817
3818
3819static Object* Runtime_StringParseInt(Arguments args) {
3820 NoHandleAllocation ha;
3821
3822 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003823 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003824
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003825 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003826
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003827 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003828 int i;
3829
3830 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003831 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003832 if (i == len) return Heap::nan_value();
3833
3834 // Compute the sign (default to +).
3835 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003836 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003837 sign = -1;
3838 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003839 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003840 i++;
3841 }
3842
3843 // Compute the radix if 0.
3844 if (radix == 0) {
3845 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003846 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847 radix = 8;
3848 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003849 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003850 if (c == 'x' || c == 'X') {
3851 radix = 16;
3852 i += 2;
3853 }
3854 }
3855 }
3856 } else if (radix == 16) {
3857 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003858 if (i + 1 < len && s->Get(i) == '0') {
3859 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003860 if (c == 'x' || c == 'X') i += 2;
3861 }
3862 }
3863
3864 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3865 double value;
3866 int end_index = StringToInt(s, i, radix, &value);
3867 if (end_index != i) {
3868 return Heap::NumberFromDouble(sign * value);
3869 }
3870 return Heap::nan_value();
3871}
3872
3873
3874static Object* Runtime_StringParseFloat(Arguments args) {
3875 NoHandleAllocation ha;
3876 CONVERT_CHECKED(String, str, args[0]);
3877
3878 // ECMA-262 section 15.1.2.3, empty string is NaN
3879 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3880
3881 // Create a number object from the value.
3882 return Heap::NumberFromDouble(value);
3883}
3884
3885
3886static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3887static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3888
3889
3890template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003891static Object* ConvertCaseHelper(String* s,
3892 int length,
3893 int input_string_length,
3894 unibrow::Mapping<Converter, 128>* mapping) {
3895 // We try this twice, once with the assumption that the result is no longer
3896 // than the input and, if that assumption breaks, again with the exact
3897 // length. This may not be pretty, but it is nicer than what was here before
3898 // and I hereby claim my vaffel-is.
3899 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003900 // Allocate the resulting string.
3901 //
3902 // NOTE: This assumes that the upper/lower case of an ascii
3903 // character is also ascii. This is currently the case, but it
3904 // might break in the future if we implement more context and locale
3905 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003906 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003907 ? Heap::AllocateRawAsciiString(length)
3908 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003909 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003910 String* result = String::cast(o);
3911 bool has_changed_character = false;
3912
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003913 // Convert all characters to upper case, assuming that they will fit
3914 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003915 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003916 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003917 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003918 // We can assume that the string is not empty
3919 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003920 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003921 bool has_next = buffer->has_more();
3922 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003923 int char_length = mapping->get(current, next, chars);
3924 if (char_length == 0) {
3925 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003926 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927 i++;
3928 } else if (char_length == 1) {
3929 // Common case: converting the letter resulted in one character.
3930 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003931 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003932 has_changed_character = true;
3933 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003934 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935 // We've assumed that the result would be as long as the
3936 // input but here is a character that converts to several
3937 // characters. No matter, we calculate the exact length
3938 // of the result and try the whole thing again.
3939 //
3940 // Note that this leaves room for optimization. We could just
3941 // memcpy what we already have to the result string. Also,
3942 // the result string is the last object allocated we could
3943 // "realloc" it and probably, in the vast majority of cases,
3944 // extend the existing string to be able to hold the full
3945 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003946 int next_length = 0;
3947 if (has_next) {
3948 next_length = mapping->get(next, 0, chars);
3949 if (next_length == 0) next_length = 1;
3950 }
3951 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003952 while (buffer->has_more()) {
3953 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003954 // NOTE: we use 0 as the next character here because, while
3955 // the next character may affect what a character converts to,
3956 // it does not in any case affect the length of what it convert
3957 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003958 int char_length = mapping->get(current, 0, chars);
3959 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003960 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003961 if (current_length > Smi::kMaxValue) {
3962 Top::context()->mark_out_of_memory();
3963 return Failure::OutOfMemoryException();
3964 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003965 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003966 // Try again with the real length.
3967 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003968 } else {
3969 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003970 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003971 i++;
3972 }
3973 has_changed_character = true;
3974 }
3975 current = next;
3976 }
3977 if (has_changed_character) {
3978 return result;
3979 } else {
3980 // If we didn't actually change anything in doing the conversion
3981 // we simple return the result and let the converted string
3982 // become garbage; there is no reason to keep two identical strings
3983 // alive.
3984 return s;
3985 }
3986}
3987
3988
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003989template <class Converter>
3990static Object* ConvertCase(Arguments args,
3991 unibrow::Mapping<Converter, 128>* mapping) {
3992 NoHandleAllocation ha;
3993
3994 CONVERT_CHECKED(String, s, args[0]);
3995 s->TryFlattenIfNotFlat();
3996
3997 int input_string_length = s->length();
3998 // Assume that the string is not empty; we need this assumption later
3999 if (input_string_length == 0) return s;
4000 int length = input_string_length;
4001
4002 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4003 if (answer->IsSmi()) {
4004 // Retry with correct length.
4005 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4006 }
4007 return answer; // This may be a failure.
4008}
4009
4010
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004011static Object* Runtime_StringToLowerCase(Arguments args) {
4012 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
4013}
4014
4015
4016static Object* Runtime_StringToUpperCase(Arguments args) {
4017 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
4018}
4019
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004020static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
4021 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
4022}
4023
4024static Object* Runtime_StringTrim(Arguments args) {
4025 NoHandleAllocation ha;
4026 ASSERT(args.length() == 3);
4027
4028 CONVERT_CHECKED(String, s, args[0]);
4029 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
4030 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
4031
4032 s->TryFlattenIfNotFlat();
4033 int length = s->length();
4034
4035 int left = 0;
4036 if (trimLeft) {
4037 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
4038 left++;
4039 }
4040 }
4041
4042 int right = length;
4043 if (trimRight) {
4044 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
4045 right--;
4046 }
4047 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004048 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004049}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004050
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00004051bool Runtime::IsUpperCaseChar(uint16_t ch) {
4052 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
4053 int char_length = to_upper_mapping.get(ch, 0, chars);
4054 return char_length == 0;
4055}
4056
4057
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004058static Object* Runtime_NumberToString(Arguments args) {
4059 NoHandleAllocation ha;
4060 ASSERT(args.length() == 1);
4061
4062 Object* number = args[0];
4063 RUNTIME_ASSERT(number->IsNumber());
4064
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004065 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004066}
4067
4068
4069static Object* Runtime_NumberToInteger(Arguments args) {
4070 NoHandleAllocation ha;
4071 ASSERT(args.length() == 1);
4072
4073 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004074 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004075 CONVERT_DOUBLE_CHECKED(number, obj);
4076 return Heap::NumberFromDouble(DoubleToInteger(number));
4077}
4078
4079
4080static Object* Runtime_NumberToJSUint32(Arguments args) {
4081 NoHandleAllocation ha;
4082 ASSERT(args.length() == 1);
4083
4084 Object* obj = args[0];
4085 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
4086 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
4087 return Heap::NumberFromUint32(number);
4088}
4089
4090
4091static Object* Runtime_NumberToJSInt32(Arguments args) {
4092 NoHandleAllocation ha;
4093 ASSERT(args.length() == 1);
4094
4095 Object* obj = args[0];
4096 if (obj->IsSmi()) return obj;
4097 CONVERT_DOUBLE_CHECKED(number, obj);
4098 return Heap::NumberFromInt32(DoubleToInt32(number));
4099}
4100
4101
ager@chromium.org870a0b62008-11-04 11:43:05 +00004102// Converts a Number to a Smi, if possible. Returns NaN if the number is not
4103// a small integer.
4104static Object* Runtime_NumberToSmi(Arguments args) {
4105 NoHandleAllocation ha;
4106 ASSERT(args.length() == 1);
4107
4108 Object* obj = args[0];
4109 if (obj->IsSmi()) {
4110 return obj;
4111 }
4112 if (obj->IsHeapNumber()) {
4113 double value = HeapNumber::cast(obj)->value();
4114 int int_value = FastD2I(value);
4115 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
4116 return Smi::FromInt(int_value);
4117 }
4118 }
4119 return Heap::nan_value();
4120}
4121
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004122
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004123static Object* Runtime_NumberAdd(Arguments args) {
4124 NoHandleAllocation ha;
4125 ASSERT(args.length() == 2);
4126
4127 CONVERT_DOUBLE_CHECKED(x, args[0]);
4128 CONVERT_DOUBLE_CHECKED(y, args[1]);
4129 return Heap::AllocateHeapNumber(x + y);
4130}
4131
4132
4133static Object* Runtime_NumberSub(Arguments args) {
4134 NoHandleAllocation ha;
4135 ASSERT(args.length() == 2);
4136
4137 CONVERT_DOUBLE_CHECKED(x, args[0]);
4138 CONVERT_DOUBLE_CHECKED(y, args[1]);
4139 return Heap::AllocateHeapNumber(x - y);
4140}
4141
4142
4143static Object* Runtime_NumberMul(Arguments args) {
4144 NoHandleAllocation ha;
4145 ASSERT(args.length() == 2);
4146
4147 CONVERT_DOUBLE_CHECKED(x, args[0]);
4148 CONVERT_DOUBLE_CHECKED(y, args[1]);
4149 return Heap::AllocateHeapNumber(x * y);
4150}
4151
4152
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004153static Object* Runtime_NumberUnaryMinus(Arguments args) {
4154 NoHandleAllocation ha;
4155 ASSERT(args.length() == 1);
4156
4157 CONVERT_DOUBLE_CHECKED(x, args[0]);
4158 return Heap::AllocateHeapNumber(-x);
4159}
4160
4161
4162static Object* Runtime_NumberDiv(Arguments args) {
4163 NoHandleAllocation ha;
4164 ASSERT(args.length() == 2);
4165
4166 CONVERT_DOUBLE_CHECKED(x, args[0]);
4167 CONVERT_DOUBLE_CHECKED(y, args[1]);
4168 return Heap::NewNumberFromDouble(x / y);
4169}
4170
4171
4172static Object* Runtime_NumberMod(Arguments args) {
4173 NoHandleAllocation ha;
4174 ASSERT(args.length() == 2);
4175
4176 CONVERT_DOUBLE_CHECKED(x, args[0]);
4177 CONVERT_DOUBLE_CHECKED(y, args[1]);
4178
ager@chromium.org3811b432009-10-28 14:53:37 +00004179 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004180 // NewNumberFromDouble may return a Smi instead of a Number object
4181 return Heap::NewNumberFromDouble(x);
4182}
4183
4184
4185static Object* Runtime_StringAdd(Arguments args) {
4186 NoHandleAllocation ha;
4187 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004188 CONVERT_CHECKED(String, str1, args[0]);
4189 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004190 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004191 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004192}
4193
4194
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004195template<typename sinkchar>
4196static inline void StringBuilderConcatHelper(String* special,
4197 sinkchar* sink,
4198 FixedArray* fixed_array,
4199 int array_length) {
4200 int position = 0;
4201 for (int i = 0; i < array_length; i++) {
4202 Object* element = fixed_array->get(i);
4203 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004204 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004205 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004206 int pos;
4207 int len;
4208 if (encoded_slice > 0) {
4209 // Position and length encoded in one smi.
4210 pos = StringBuilderSubstringPosition::decode(encoded_slice);
4211 len = StringBuilderSubstringLength::decode(encoded_slice);
4212 } else {
4213 // Position and length encoded in two smis.
4214 Object* obj = fixed_array->get(++i);
4215 ASSERT(obj->IsSmi());
4216 pos = Smi::cast(obj)->value();
4217 len = -encoded_slice;
4218 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00004219 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004220 sink + position,
4221 pos,
4222 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004223 position += len;
4224 } else {
4225 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004226 int element_length = string->length();
4227 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004228 position += element_length;
4229 }
4230 }
4231}
4232
4233
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004234static Object* Runtime_StringBuilderConcat(Arguments args) {
4235 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004236 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004237 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004238 if (!args[1]->IsSmi()) {
4239 Top::context()->mark_out_of_memory();
4240 return Failure::OutOfMemoryException();
4241 }
4242 int array_length = Smi::cast(args[1])->value();
4243 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004244
4245 // This assumption is used by the slice encoding in one or two smis.
4246 ASSERT(Smi::kMaxValue >= String::kMaxLength);
4247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004248 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004249 if (!array->HasFastElements()) {
4250 return Top::Throw(Heap::illegal_argument_symbol());
4251 }
4252 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004253 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004254 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004255 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004256
4257 if (array_length == 0) {
4258 return Heap::empty_string();
4259 } else if (array_length == 1) {
4260 Object* first = fixed_array->get(0);
4261 if (first->IsString()) return first;
4262 }
4263
ager@chromium.org5ec48922009-05-05 07:25:34 +00004264 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004265 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004266 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004267 for (int i = 0; i < array_length; i++) {
4268 Object* elt = fixed_array->get(i);
4269 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004270 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004271 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004272 if (len > 0) {
4273 // Position and length encoded in one smi.
4274 int pos = len >> 11;
4275 len &= 0x7ff;
4276 if (pos + len > special_length) {
4277 return Top::Throw(Heap::illegal_argument_symbol());
4278 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004279 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004280 } else {
4281 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004282 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004283 // Get the position and check that it is also a smi.
4284 i++;
4285 if (i >= array_length) {
4286 return Top::Throw(Heap::illegal_argument_symbol());
4287 }
4288 Object* pos = fixed_array->get(i);
4289 if (!pos->IsSmi()) {
4290 return Top::Throw(Heap::illegal_argument_symbol());
4291 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004292 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004293 } else if (elt->IsString()) {
4294 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004295 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004296 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004297 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004298 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004299 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004300 } else {
4301 return Top::Throw(Heap::illegal_argument_symbol());
4302 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004303 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004304 Top::context()->mark_out_of_memory();
4305 return Failure::OutOfMemoryException();
4306 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004307 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004308 }
4309
4310 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004311 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004312
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004313 if (ascii) {
4314 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004315 if (object->IsFailure()) return object;
4316 SeqAsciiString* answer = SeqAsciiString::cast(object);
4317 StringBuilderConcatHelper(special,
4318 answer->GetChars(),
4319 fixed_array,
4320 array_length);
4321 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004322 } else {
4323 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004324 if (object->IsFailure()) return object;
4325 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
4326 StringBuilderConcatHelper(special,
4327 answer->GetChars(),
4328 fixed_array,
4329 array_length);
4330 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004331 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004332}
4333
4334
4335static Object* Runtime_NumberOr(Arguments args) {
4336 NoHandleAllocation ha;
4337 ASSERT(args.length() == 2);
4338
4339 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4340 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4341 return Heap::NumberFromInt32(x | y);
4342}
4343
4344
4345static Object* Runtime_NumberAnd(Arguments args) {
4346 NoHandleAllocation ha;
4347 ASSERT(args.length() == 2);
4348
4349 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4350 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4351 return Heap::NumberFromInt32(x & y);
4352}
4353
4354
4355static Object* Runtime_NumberXor(Arguments args) {
4356 NoHandleAllocation ha;
4357 ASSERT(args.length() == 2);
4358
4359 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4360 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4361 return Heap::NumberFromInt32(x ^ y);
4362}
4363
4364
4365static Object* Runtime_NumberNot(Arguments args) {
4366 NoHandleAllocation ha;
4367 ASSERT(args.length() == 1);
4368
4369 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4370 return Heap::NumberFromInt32(~x);
4371}
4372
4373
4374static Object* Runtime_NumberShl(Arguments args) {
4375 NoHandleAllocation ha;
4376 ASSERT(args.length() == 2);
4377
4378 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4379 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4380 return Heap::NumberFromInt32(x << (y & 0x1f));
4381}
4382
4383
4384static Object* Runtime_NumberShr(Arguments args) {
4385 NoHandleAllocation ha;
4386 ASSERT(args.length() == 2);
4387
4388 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
4389 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4390 return Heap::NumberFromUint32(x >> (y & 0x1f));
4391}
4392
4393
4394static Object* Runtime_NumberSar(Arguments args) {
4395 NoHandleAllocation ha;
4396 ASSERT(args.length() == 2);
4397
4398 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4399 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4400 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
4401}
4402
4403
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004404static Object* Runtime_NumberEquals(Arguments args) {
4405 NoHandleAllocation ha;
4406 ASSERT(args.length() == 2);
4407
4408 CONVERT_DOUBLE_CHECKED(x, args[0]);
4409 CONVERT_DOUBLE_CHECKED(y, args[1]);
4410 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
4411 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
4412 if (x == y) return Smi::FromInt(EQUAL);
4413 Object* result;
4414 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
4415 result = Smi::FromInt(EQUAL);
4416 } else {
4417 result = Smi::FromInt(NOT_EQUAL);
4418 }
4419 return result;
4420}
4421
4422
4423static Object* Runtime_StringEquals(Arguments args) {
4424 NoHandleAllocation ha;
4425 ASSERT(args.length() == 2);
4426
4427 CONVERT_CHECKED(String, x, args[0]);
4428 CONVERT_CHECKED(String, y, args[1]);
4429
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004430 bool not_equal = !x->Equals(y);
4431 // This is slightly convoluted because the value that signifies
4432 // equality is 0 and inequality is 1 so we have to negate the result
4433 // from String::Equals.
4434 ASSERT(not_equal == 0 || not_equal == 1);
4435 STATIC_CHECK(EQUAL == 0);
4436 STATIC_CHECK(NOT_EQUAL == 1);
4437 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004438}
4439
4440
4441static Object* Runtime_NumberCompare(Arguments args) {
4442 NoHandleAllocation ha;
4443 ASSERT(args.length() == 3);
4444
4445 CONVERT_DOUBLE_CHECKED(x, args[0]);
4446 CONVERT_DOUBLE_CHECKED(y, args[1]);
4447 if (isnan(x) || isnan(y)) return args[2];
4448 if (x == y) return Smi::FromInt(EQUAL);
4449 if (isless(x, y)) return Smi::FromInt(LESS);
4450 return Smi::FromInt(GREATER);
4451}
4452
4453
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004454// Compare two Smis as if they were converted to strings and then
4455// compared lexicographically.
4456static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4457 NoHandleAllocation ha;
4458 ASSERT(args.length() == 2);
4459
4460 // Arrays for the individual characters of the two Smis. Smis are
4461 // 31 bit integers and 10 decimal digits are therefore enough.
4462 static int x_elms[10];
4463 static int y_elms[10];
4464
4465 // Extract the integer values from the Smis.
4466 CONVERT_CHECKED(Smi, x, args[0]);
4467 CONVERT_CHECKED(Smi, y, args[1]);
4468 int x_value = x->value();
4469 int y_value = y->value();
4470
4471 // If the integers are equal so are the string representations.
4472 if (x_value == y_value) return Smi::FromInt(EQUAL);
4473
4474 // If one of the integers are zero the normal integer order is the
4475 // same as the lexicographic order of the string representations.
4476 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4477
ager@chromium.org32912102009-01-16 10:38:43 +00004478 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004479 // smallest because the char code of '-' is less than the char code
4480 // of any digit. Otherwise, we make both values positive.
4481 if (x_value < 0 || y_value < 0) {
4482 if (y_value >= 0) return Smi::FromInt(LESS);
4483 if (x_value >= 0) return Smi::FromInt(GREATER);
4484 x_value = -x_value;
4485 y_value = -y_value;
4486 }
4487
4488 // Convert the integers to arrays of their decimal digits.
4489 int x_index = 0;
4490 int y_index = 0;
4491 while (x_value > 0) {
4492 x_elms[x_index++] = x_value % 10;
4493 x_value /= 10;
4494 }
4495 while (y_value > 0) {
4496 y_elms[y_index++] = y_value % 10;
4497 y_value /= 10;
4498 }
4499
4500 // Loop through the arrays of decimal digits finding the first place
4501 // where they differ.
4502 while (--x_index >= 0 && --y_index >= 0) {
4503 int diff = x_elms[x_index] - y_elms[y_index];
4504 if (diff != 0) return Smi::FromInt(diff);
4505 }
4506
4507 // If one array is a suffix of the other array, the longest array is
4508 // the representation of the largest of the Smis in the
4509 // lexicographic ordering.
4510 return Smi::FromInt(x_index - y_index);
4511}
4512
4513
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004514static Object* Runtime_StringCompare(Arguments args) {
4515 NoHandleAllocation ha;
4516 ASSERT(args.length() == 2);
4517
4518 CONVERT_CHECKED(String, x, args[0]);
4519 CONVERT_CHECKED(String, y, args[1]);
4520
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004521 Counters::string_compare_runtime.Increment();
4522
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004523 // A few fast case tests before we flatten.
4524 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004525 if (y->length() == 0) {
4526 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004527 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004528 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004529 return Smi::FromInt(LESS);
4530 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004531
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004532 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004533 if (d < 0) return Smi::FromInt(LESS);
4534 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004535
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004536 x->TryFlattenIfNotFlat();
4537 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538
4539 static StringInputBuffer bufx;
4540 static StringInputBuffer bufy;
4541 bufx.Reset(x);
4542 bufy.Reset(y);
4543 while (bufx.has_more() && bufy.has_more()) {
4544 int d = bufx.GetNext() - bufy.GetNext();
4545 if (d < 0) return Smi::FromInt(LESS);
4546 else if (d > 0) return Smi::FromInt(GREATER);
4547 }
4548
4549 // x is (non-trivial) prefix of y:
4550 if (bufy.has_more()) return Smi::FromInt(LESS);
4551 // y is prefix of x:
4552 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4553}
4554
4555
4556static Object* Runtime_Math_abs(Arguments args) {
4557 NoHandleAllocation ha;
4558 ASSERT(args.length() == 1);
4559
4560 CONVERT_DOUBLE_CHECKED(x, args[0]);
4561 return Heap::AllocateHeapNumber(fabs(x));
4562}
4563
4564
4565static Object* Runtime_Math_acos(Arguments args) {
4566 NoHandleAllocation ha;
4567 ASSERT(args.length() == 1);
4568
4569 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004570 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004571}
4572
4573
4574static Object* Runtime_Math_asin(Arguments args) {
4575 NoHandleAllocation ha;
4576 ASSERT(args.length() == 1);
4577
4578 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004579 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004580}
4581
4582
4583static Object* Runtime_Math_atan(Arguments args) {
4584 NoHandleAllocation ha;
4585 ASSERT(args.length() == 1);
4586
4587 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004588 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004589}
4590
4591
4592static Object* Runtime_Math_atan2(Arguments args) {
4593 NoHandleAllocation ha;
4594 ASSERT(args.length() == 2);
4595
4596 CONVERT_DOUBLE_CHECKED(x, args[0]);
4597 CONVERT_DOUBLE_CHECKED(y, args[1]);
4598 double result;
4599 if (isinf(x) && isinf(y)) {
4600 // Make sure that the result in case of two infinite arguments
4601 // is a multiple of Pi / 4. The sign of the result is determined
4602 // by the first argument (x) and the sign of the second argument
4603 // determines the multiplier: one or three.
4604 static double kPiDividedBy4 = 0.78539816339744830962;
4605 int multiplier = (x < 0) ? -1 : 1;
4606 if (y < 0) multiplier *= 3;
4607 result = multiplier * kPiDividedBy4;
4608 } else {
4609 result = atan2(x, y);
4610 }
4611 return Heap::AllocateHeapNumber(result);
4612}
4613
4614
4615static Object* Runtime_Math_ceil(Arguments args) {
4616 NoHandleAllocation ha;
4617 ASSERT(args.length() == 1);
4618
4619 CONVERT_DOUBLE_CHECKED(x, args[0]);
4620 return Heap::NumberFromDouble(ceiling(x));
4621}
4622
4623
4624static Object* Runtime_Math_cos(Arguments args) {
4625 NoHandleAllocation ha;
4626 ASSERT(args.length() == 1);
4627
4628 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004629 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004630}
4631
4632
4633static Object* Runtime_Math_exp(Arguments args) {
4634 NoHandleAllocation ha;
4635 ASSERT(args.length() == 1);
4636
4637 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004638 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004639}
4640
4641
4642static Object* Runtime_Math_floor(Arguments args) {
4643 NoHandleAllocation ha;
4644 ASSERT(args.length() == 1);
4645
4646 CONVERT_DOUBLE_CHECKED(x, args[0]);
4647 return Heap::NumberFromDouble(floor(x));
4648}
4649
4650
4651static Object* Runtime_Math_log(Arguments args) {
4652 NoHandleAllocation ha;
4653 ASSERT(args.length() == 1);
4654
4655 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004656 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004657}
4658
4659
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004660// Helper function to compute x^y, where y is known to be an
4661// integer. Uses binary decomposition to limit the number of
4662// multiplications; see the discussion in "Hacker's Delight" by Henry
4663// S. Warren, Jr., figure 11-6, page 213.
4664static double powi(double x, int y) {
4665 ASSERT(y != kMinInt);
4666 unsigned n = (y < 0) ? -y : y;
4667 double m = x;
4668 double p = 1;
4669 while (true) {
4670 if ((n & 1) != 0) p *= m;
4671 n >>= 1;
4672 if (n == 0) {
4673 if (y < 0) {
4674 // Unfortunately, we have to be careful when p has reached
4675 // infinity in the computation, because sometimes the higher
4676 // internal precision in the pow() implementation would have
4677 // given us a finite p. This happens very rarely.
4678 double result = 1.0 / p;
4679 return (result == 0 && isinf(p))
4680 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4681 : result;
4682 } else {
4683 return p;
4684 }
4685 }
4686 m *= m;
4687 }
4688}
4689
4690
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691static Object* Runtime_Math_pow(Arguments args) {
4692 NoHandleAllocation ha;
4693 ASSERT(args.length() == 2);
4694
4695 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004696
4697 // If the second argument is a smi, it is much faster to call the
4698 // custom powi() function than the generic pow().
4699 if (args[1]->IsSmi()) {
4700 int y = Smi::cast(args[1])->value();
4701 return Heap::AllocateHeapNumber(powi(x, y));
4702 }
4703
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004704 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004705
4706 if (!isinf(x)) {
4707 if (y == 0.5) {
4708 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4709 // square root of a number. To speed up such computations, we
4710 // explictly check for this case and use the sqrt() function
4711 // which is faster than pow().
4712 return Heap::AllocateHeapNumber(sqrt(x));
4713 } else if (y == -0.5) {
4714 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4715 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4716 }
4717 }
4718
4719 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004720 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004721 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4722 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004723 } else {
4724 return Heap::AllocateHeapNumber(pow(x, y));
4725 }
4726}
4727
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004728
4729static Object* Runtime_Math_round(Arguments args) {
4730 NoHandleAllocation ha;
4731 ASSERT(args.length() == 1);
4732
4733 CONVERT_DOUBLE_CHECKED(x, args[0]);
4734 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004735 double integer = ceil(x);
4736 if (integer - x > 0.5) { integer -= 1.0; }
4737 return Heap::NumberFromDouble(integer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004738}
4739
4740
4741static Object* Runtime_Math_sin(Arguments args) {
4742 NoHandleAllocation ha;
4743 ASSERT(args.length() == 1);
4744
4745 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004746 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004747}
4748
4749
4750static Object* Runtime_Math_sqrt(Arguments args) {
4751 NoHandleAllocation ha;
4752 ASSERT(args.length() == 1);
4753
4754 CONVERT_DOUBLE_CHECKED(x, args[0]);
4755 return Heap::AllocateHeapNumber(sqrt(x));
4756}
4757
4758
4759static Object* Runtime_Math_tan(Arguments args) {
4760 NoHandleAllocation ha;
4761 ASSERT(args.length() == 1);
4762
4763 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004764 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765}
4766
4767
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004768static Object* Runtime_NewArgumentsFast(Arguments args) {
4769 NoHandleAllocation ha;
4770 ASSERT(args.length() == 3);
4771
4772 JSFunction* callee = JSFunction::cast(args[0]);
4773 Object** parameters = reinterpret_cast<Object**>(args[1]);
4774 const int length = Smi::cast(args[2])->value();
4775
4776 Object* result = Heap::AllocateArgumentsObject(callee, length);
4777 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004778 // Allocate the elements if needed.
4779 if (length > 0) {
4780 // Allocate the fixed array.
4781 Object* obj = Heap::AllocateRawFixedArray(length);
4782 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004783
4784 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004785 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4786 FixedArray* array = FixedArray::cast(obj);
4787 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004788
4789 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004790 for (int i = 0; i < length; i++) {
4791 array->set(i, *--parameters, mode);
4792 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004793 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004794 }
4795 return result;
4796}
4797
4798
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004799static Object* Runtime_NewClosure(Arguments args) {
4800 HandleScope scope;
4801 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00004802 CONVERT_ARG_CHECKED(Context, context, 0);
4803 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004804
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004805 PretenureFlag pretenure = (context->global_context() == *context)
4806 ? TENURED // Allocate global closures in old space.
4807 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004808 Handle<JSFunction> result =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00004809 Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004810 return *result;
4811}
4812
4813
ager@chromium.org5c838252010-02-19 08:53:10 +00004814static Code* ComputeConstructStub(Handle<JSFunction> function) {
4815 Handle<Object> prototype = Factory::null_value();
4816 if (function->has_instance_prototype()) {
4817 prototype = Handle<Object>(function->instance_prototype());
4818 }
4819 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004820 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00004821 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004822 if (code->IsFailure()) {
4823 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4824 }
4825 return Code::cast(code);
4826 }
4827
ager@chromium.org5c838252010-02-19 08:53:10 +00004828 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004829}
4830
4831
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004832static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004833 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004834 ASSERT(args.length() == 1);
4835
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004836 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004837
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004838 // If the constructor isn't a proper function we throw a type error.
4839 if (!constructor->IsJSFunction()) {
4840 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4841 Handle<Object> type_error =
4842 Factory::NewTypeError("not_constructor", arguments);
4843 return Top::Throw(*type_error);
4844 }
4845
4846 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004847#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004848 // Handle stepping into constructors if step into is active.
4849 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004850 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004851 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004852#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004854 if (function->has_initial_map()) {
4855 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004856 // The 'Function' function ignores the receiver object when
4857 // called using 'new' and creates a new JSFunction object that
4858 // is returned. The receiver object is only used for error
4859 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004860 // JSFunction. Factory::NewJSObject() should not be used to
4861 // allocate JSFunctions since it does not properly initialize
4862 // the shared part of the function. Since the receiver is
4863 // ignored anyway, we use the global object as the receiver
4864 // instead of a new JSFunction object. This way, errors are
4865 // reported the same way whether or not 'Function' is called
4866 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004867 return Top::context()->global();
4868 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004869 }
4870
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004871 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004872 Handle<SharedFunctionInfo> shared(function->shared());
4873 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004874
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004875 bool first_allocation = !function->has_initial_map();
4876 Handle<JSObject> result = Factory::NewJSObject(function);
4877 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004878 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00004879 ComputeConstructStub(Handle<JSFunction>(function)));
4880 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004881 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004882
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004883 Counters::constructed_objects.Increment();
4884 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004885
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004886 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004887}
4888
4889
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004890static Object* Runtime_LazyCompile(Arguments args) {
4891 HandleScope scope;
4892 ASSERT(args.length() == 1);
4893
4894 Handle<JSFunction> function = args.at<JSFunction>(0);
4895#ifdef DEBUG
4896 if (FLAG_trace_lazy) {
4897 PrintF("[lazy: ");
4898 function->shared()->name()->Print();
4899 PrintF("]\n");
4900 }
4901#endif
4902
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004903 // Compile the target function. Here we compile using CompileLazyInLoop in
4904 // order to get the optimized version. This helps code like delta-blue
4905 // that calls performance-critical routines through constructors. A
4906 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4907 // direct call. Since the in-loop tracking takes place through CallICs
4908 // this means that things called through constructors are never known to
4909 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004910 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004911 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004912 return Failure::Exception();
4913 }
4914
4915 return function->code();
4916}
4917
4918
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004919static Object* Runtime_GetFunctionDelegate(Arguments args) {
4920 HandleScope scope;
4921 ASSERT(args.length() == 1);
4922 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4923 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4924}
4925
4926
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004927static Object* Runtime_GetConstructorDelegate(Arguments args) {
4928 HandleScope scope;
4929 ASSERT(args.length() == 1);
4930 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4931 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4932}
4933
4934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004935static Object* Runtime_NewContext(Arguments args) {
4936 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004937 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004938
kasper.lund7276f142008-07-30 08:49:36 +00004939 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004940 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4941 Object* result = Heap::AllocateFunctionContext(length, function);
4942 if (result->IsFailure()) return result;
4943
4944 Top::set_context(Context::cast(result));
4945
kasper.lund7276f142008-07-30 08:49:36 +00004946 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004947}
4948
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004949static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004950 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004951 Object* js_object = object;
4952 if (!js_object->IsJSObject()) {
4953 js_object = js_object->ToObject();
4954 if (js_object->IsFailure()) {
4955 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004956 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004957 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004958 Handle<Object> result =
4959 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4960 return Top::Throw(*result);
4961 }
4962 }
4963
4964 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004965 Heap::AllocateWithContext(Top::context(),
4966 JSObject::cast(js_object),
4967 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004968 if (result->IsFailure()) return result;
4969
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004970 Context* context = Context::cast(result);
4971 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004972
kasper.lund7276f142008-07-30 08:49:36 +00004973 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004974}
4975
4976
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004977static Object* Runtime_PushContext(Arguments args) {
4978 NoHandleAllocation ha;
4979 ASSERT(args.length() == 1);
4980 return PushContextHelper(args[0], false);
4981}
4982
4983
4984static Object* Runtime_PushCatchContext(Arguments args) {
4985 NoHandleAllocation ha;
4986 ASSERT(args.length() == 1);
4987 return PushContextHelper(args[0], true);
4988}
4989
4990
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004991static Object* Runtime_LookupContext(Arguments args) {
4992 HandleScope scope;
4993 ASSERT(args.length() == 2);
4994
4995 CONVERT_ARG_CHECKED(Context, context, 0);
4996 CONVERT_ARG_CHECKED(String, name, 1);
4997
4998 int index;
4999 PropertyAttributes attributes;
5000 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005001 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005002 context->Lookup(name, flags, &index, &attributes);
5003
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005004 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005005 ASSERT(holder->IsJSObject());
5006 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005007 }
5008
5009 // No intermediate context found. Use global object by default.
5010 return Top::context()->global();
5011}
5012
5013
ager@chromium.orga1645e22009-09-09 19:27:10 +00005014// A mechanism to return a pair of Object pointers in registers (if possible).
5015// How this is achieved is calling convention-dependent.
5016// All currently supported x86 compiles uses calling conventions that are cdecl
5017// variants where a 64-bit value is returned in two 32-bit registers
5018// (edx:eax on ia32, r1:r0 on ARM).
5019// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
5020// In Win64 calling convention, a struct of two pointers is returned in memory,
5021// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005022#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005023struct ObjectPair {
5024 Object* x;
5025 Object* y;
5026};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005027
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005028static inline ObjectPair MakePair(Object* x, Object* y) {
5029 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005030 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
5031 // In Win64 they are assigned to a hidden first argument.
5032 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005033}
5034#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005035typedef uint64_t ObjectPair;
5036static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005037 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005038 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005039}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005040#endif
5041
5042
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005043static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005044 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
5045 USE(attributes);
5046 return x->IsTheHole() ? Heap::undefined_value() : x;
5047}
5048
5049
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005050static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
5051 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005052 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005053 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005054 JSFunction* context_extension_function =
5055 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005056 // If the holder isn't a context extension object, we just return it
5057 // as the receiver. This allows arguments objects to be used as
5058 // receivers, but only if they are put in the context scope chain
5059 // explicitly via a with-statement.
5060 Object* constructor = holder->map()->constructor();
5061 if (constructor != context_extension_function) return holder;
5062 // Fall back to using the global object as the receiver if the
5063 // property turns out to be a local variable allocated in a context
5064 // extension object - introduced via eval.
5065 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005066}
5067
5068
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005069static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005070 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00005071 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005072
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005073 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00005074 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005075 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005076 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005077 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005078
5079 int index;
5080 PropertyAttributes attributes;
5081 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005082 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005083 context->Lookup(name, flags, &index, &attributes);
5084
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005085 // If the index is non-negative, the slot has been found in a local
5086 // variable or a parameter. Read it from the context object or the
5087 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005088 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005089 // If the "property" we were looking for is a local variable or an
5090 // argument in a context, the receiver is the global object; see
5091 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
5092 JSObject* receiver = Top::context()->global()->global_receiver();
5093 Object* value = (holder->IsContext())
5094 ? Context::cast(*holder)->get(index)
5095 : JSObject::cast(*holder)->GetElement(index);
5096 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005097 }
5098
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005099 // If the holder is found, we read the property from it.
5100 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005101 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005102 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005103 JSObject* receiver;
5104 if (object->IsGlobalObject()) {
5105 receiver = GlobalObject::cast(object)->global_receiver();
5106 } else if (context->is_exception_holder(*holder)) {
5107 receiver = Top::context()->global()->global_receiver();
5108 } else {
5109 receiver = ComputeReceiverForNonGlobal(object);
5110 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005111 // No need to unhole the value here. This is taken care of by the
5112 // GetProperty function.
5113 Object* value = object->GetProperty(*name);
5114 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005115 }
5116
5117 if (throw_error) {
5118 // The property doesn't exist - throw exception.
5119 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005120 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005121 return MakePair(Top::Throw(*reference_error), NULL);
5122 } else {
5123 // The property doesn't exist - return undefined
5124 return MakePair(Heap::undefined_value(), Heap::undefined_value());
5125 }
5126}
5127
5128
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005129static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005130 return LoadContextSlotHelper(args, true);
5131}
5132
5133
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005134static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005135 return LoadContextSlotHelper(args, false);
5136}
5137
5138
5139static Object* Runtime_StoreContextSlot(Arguments args) {
5140 HandleScope scope;
5141 ASSERT(args.length() == 3);
5142
5143 Handle<Object> value(args[0]);
5144 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005145 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005146
5147 int index;
5148 PropertyAttributes attributes;
5149 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005150 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005151 context->Lookup(name, flags, &index, &attributes);
5152
5153 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005154 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005155 // Ignore if read_only variable.
5156 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005157 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005158 }
5159 } else {
5160 ASSERT((attributes & READ_ONLY) == 0);
5161 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005162 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005163 USE(result);
5164 ASSERT(!result->IsFailure());
5165 }
5166 return *value;
5167 }
5168
5169 // Slow case: The property is not in a FixedArray context.
5170 // It is either in an JSObject extension context or it was not found.
5171 Handle<JSObject> context_ext;
5172
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005173 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005174 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005175 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005176 } else {
5177 // The property was not found. It needs to be stored in the global context.
5178 ASSERT(attributes == ABSENT);
5179 attributes = NONE;
5180 context_ext = Handle<JSObject>(Top::context()->global());
5181 }
5182
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005183 // Set the property, but ignore if read_only variable on the context
5184 // extension object itself.
5185 if ((attributes & READ_ONLY) == 0 ||
5186 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005187 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
5188 if (set.is_null()) {
5189 // Failure::Exception is converted to a null handle in the
5190 // handle-based methods such as SetProperty. We therefore need
5191 // to convert null handles back to exceptions.
5192 ASSERT(Top::has_pending_exception());
5193 return Failure::Exception();
5194 }
5195 }
5196 return *value;
5197}
5198
5199
5200static Object* Runtime_Throw(Arguments args) {
5201 HandleScope scope;
5202 ASSERT(args.length() == 1);
5203
5204 return Top::Throw(args[0]);
5205}
5206
5207
5208static Object* Runtime_ReThrow(Arguments args) {
5209 HandleScope scope;
5210 ASSERT(args.length() == 1);
5211
5212 return Top::ReThrow(args[0]);
5213}
5214
5215
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005216static Object* Runtime_PromoteScheduledException(Arguments args) {
5217 ASSERT_EQ(0, args.length());
5218 return Top::PromoteScheduledException();
5219}
5220
5221
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005222static Object* Runtime_ThrowReferenceError(Arguments args) {
5223 HandleScope scope;
5224 ASSERT(args.length() == 1);
5225
5226 Handle<Object> name(args[0]);
5227 Handle<Object> reference_error =
5228 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5229 return Top::Throw(*reference_error);
5230}
5231
5232
5233static Object* Runtime_StackOverflow(Arguments args) {
5234 NoHandleAllocation na;
5235 return Top::StackOverflow();
5236}
5237
5238
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005239static Object* Runtime_StackGuard(Arguments args) {
5240 ASSERT(args.length() == 1);
5241
5242 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005243 if (StackGuard::IsStackOverflow()) {
5244 return Runtime_StackOverflow(args);
5245 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005246
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005247 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005248}
5249
5250
5251// NOTE: These PrintXXX functions are defined for all builds (not just
5252// DEBUG builds) because we may want to be able to trace function
5253// calls in all modes.
5254static void PrintString(String* str) {
5255 // not uncommon to have empty strings
5256 if (str->length() > 0) {
5257 SmartPointer<char> s =
5258 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
5259 PrintF("%s", *s);
5260 }
5261}
5262
5263
5264static void PrintObject(Object* obj) {
5265 if (obj->IsSmi()) {
5266 PrintF("%d", Smi::cast(obj)->value());
5267 } else if (obj->IsString() || obj->IsSymbol()) {
5268 PrintString(String::cast(obj));
5269 } else if (obj->IsNumber()) {
5270 PrintF("%g", obj->Number());
5271 } else if (obj->IsFailure()) {
5272 PrintF("<failure>");
5273 } else if (obj->IsUndefined()) {
5274 PrintF("<undefined>");
5275 } else if (obj->IsNull()) {
5276 PrintF("<null>");
5277 } else if (obj->IsTrue()) {
5278 PrintF("<true>");
5279 } else if (obj->IsFalse()) {
5280 PrintF("<false>");
5281 } else {
5282 PrintF("%p", obj);
5283 }
5284}
5285
5286
5287static int StackSize() {
5288 int n = 0;
5289 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
5290 return n;
5291}
5292
5293
5294static void PrintTransition(Object* result) {
5295 // indentation
5296 { const int nmax = 80;
5297 int n = StackSize();
5298 if (n <= nmax)
5299 PrintF("%4d:%*s", n, n, "");
5300 else
5301 PrintF("%4d:%*s", n, nmax, "...");
5302 }
5303
5304 if (result == NULL) {
5305 // constructor calls
5306 JavaScriptFrameIterator it;
5307 JavaScriptFrame* frame = it.frame();
5308 if (frame->IsConstructor()) PrintF("new ");
5309 // function name
5310 Object* fun = frame->function();
5311 if (fun->IsJSFunction()) {
5312 PrintObject(JSFunction::cast(fun)->shared()->name());
5313 } else {
5314 PrintObject(fun);
5315 }
5316 // function arguments
5317 // (we are intentionally only printing the actually
5318 // supplied parameters, not all parameters required)
5319 PrintF("(this=");
5320 PrintObject(frame->receiver());
5321 const int length = frame->GetProvidedParametersCount();
5322 for (int i = 0; i < length; i++) {
5323 PrintF(", ");
5324 PrintObject(frame->GetParameter(i));
5325 }
5326 PrintF(") {\n");
5327
5328 } else {
5329 // function result
5330 PrintF("} -> ");
5331 PrintObject(result);
5332 PrintF("\n");
5333 }
5334}
5335
5336
5337static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005338 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005339 NoHandleAllocation ha;
5340 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005341 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005342}
5343
5344
5345static Object* Runtime_TraceExit(Arguments args) {
5346 NoHandleAllocation ha;
5347 PrintTransition(args[0]);
5348 return args[0]; // return TOS
5349}
5350
5351
5352static Object* Runtime_DebugPrint(Arguments args) {
5353 NoHandleAllocation ha;
5354 ASSERT(args.length() == 1);
5355
5356#ifdef DEBUG
5357 if (args[0]->IsString()) {
5358 // If we have a string, assume it's a code "marker"
5359 // and print some interesting cpu debugging info.
5360 JavaScriptFrameIterator it;
5361 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005362 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
5363 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005364 } else {
5365 PrintF("DebugPrint: ");
5366 }
5367 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005368 if (args[0]->IsHeapObject()) {
5369 HeapObject::cast(args[0])->map()->Print();
5370 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005371#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005372 // ShortPrint is available in release mode. Print is not.
5373 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005374#endif
5375 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00005376 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005377
5378 return args[0]; // return TOS
5379}
5380
5381
5382static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005383 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005384 NoHandleAllocation ha;
5385 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005386 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005387}
5388
5389
mads.s.ager31e71382008-08-13 09:32:07 +00005390static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005391 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005392 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005393
5394 // According to ECMA-262, section 15.9.1, page 117, the precision of
5395 // the number in a Date object representing a particular instant in
5396 // time is milliseconds. Therefore, we floor the result of getting
5397 // the OS time.
5398 double millis = floor(OS::TimeCurrentMillis());
5399 return Heap::NumberFromDouble(millis);
5400}
5401
5402
5403static Object* Runtime_DateParseString(Arguments args) {
5404 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005405 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005406
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005407 CONVERT_ARG_CHECKED(String, str, 0);
5408 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005409
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005410 CONVERT_ARG_CHECKED(JSArray, output, 1);
5411 RUNTIME_ASSERT(output->HasFastElements());
5412
5413 AssertNoAllocation no_allocation;
5414
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005415 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005416 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5417 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005418 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005419 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005420 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005421 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005422 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5423 }
5424
5425 if (result) {
5426 return *output;
5427 } else {
5428 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005429 }
5430}
5431
5432
5433static Object* Runtime_DateLocalTimezone(Arguments args) {
5434 NoHandleAllocation ha;
5435 ASSERT(args.length() == 1);
5436
5437 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005438 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005439 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5440}
5441
5442
5443static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5444 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005445 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005446
5447 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5448}
5449
5450
5451static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5452 NoHandleAllocation ha;
5453 ASSERT(args.length() == 1);
5454
5455 CONVERT_DOUBLE_CHECKED(x, args[0]);
5456 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5457}
5458
5459
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005460static Object* Runtime_NumberIsFinite(Arguments args) {
5461 NoHandleAllocation ha;
5462 ASSERT(args.length() == 1);
5463
5464 CONVERT_DOUBLE_CHECKED(value, args[0]);
5465 Object* result;
5466 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5467 result = Heap::false_value();
5468 } else {
5469 result = Heap::true_value();
5470 }
5471 return result;
5472}
5473
5474
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005475static Object* Runtime_GlobalReceiver(Arguments args) {
5476 ASSERT(args.length() == 1);
5477 Object* global = args[0];
5478 if (!global->IsJSGlobalObject()) return Heap::null_value();
5479 return JSGlobalObject::cast(global)->global_receiver();
5480}
5481
5482
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005483static Object* Runtime_CompileString(Arguments args) {
5484 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005485 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005486 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005487 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005488
ager@chromium.org381abbb2009-02-25 13:23:22 +00005489 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005490 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005491 Compiler::ValidationState validate = (is_json->IsTrue())
5492 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005493 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5494 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005495 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005496 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005497 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 Handle<JSFunction> fun =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005499 Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005500 return *fun;
5501}
5502
5503
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005504static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
5505 ASSERT(args.length() == 3);
5506 if (!args[0]->IsJSFunction()) {
5507 return MakePair(Top::ThrowIllegalOperation(), NULL);
5508 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005509
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005510 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005511 Handle<JSFunction> callee = args.at<JSFunction>(0);
5512 Handle<Object> receiver; // Will be overwritten.
5513
5514 // Compute the calling context.
5515 Handle<Context> context = Handle<Context>(Top::context());
5516#ifdef DEBUG
5517 // Make sure Top::context() agrees with the old code that traversed
5518 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005519 StackFrameLocator locator;
5520 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005521 ASSERT(Context::cast(frame->context()) == *context);
5522#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005523
5524 // Find where the 'eval' symbol is bound. It is unaliased only if
5525 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005526 int index = -1;
5527 PropertyAttributes attributes = ABSENT;
5528 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005529 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5530 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005531 // Stop search when eval is found or when the global context is
5532 // reached.
5533 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005534 if (context->is_function_context()) {
5535 context = Handle<Context>(Context::cast(context->closure()->context()));
5536 } else {
5537 context = Handle<Context>(context->previous());
5538 }
5539 }
5540
iposva@chromium.org245aa852009-02-10 00:49:54 +00005541 // If eval could not be resolved, it has been deleted and we need to
5542 // throw a reference error.
5543 if (attributes == ABSENT) {
5544 Handle<Object> name = Factory::eval_symbol();
5545 Handle<Object> reference_error =
5546 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005547 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005548 }
5549
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005550 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005551 // 'eval' is not bound in the global context. Just call the function
5552 // with the given arguments. This is not necessarily the global eval.
5553 if (receiver->IsContext()) {
5554 context = Handle<Context>::cast(receiver);
5555 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005556 } else if (receiver->IsJSContextExtensionObject()) {
5557 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005558 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005559 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005560 }
5561
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005562 // 'eval' is bound in the global context, but it may have been overwritten.
5563 // Compare it to the builtin 'GlobalEval' function to make sure.
5564 if (*callee != Top::global_context()->global_eval_fun() ||
5565 !args[1]->IsString()) {
5566 return MakePair(*callee, Top::context()->global()->global_receiver());
5567 }
5568
5569 // Deal with a normal eval call with a string argument. Compile it
5570 // and return the compiled function bound in the local context.
5571 Handle<String> source = args.at<String>(1);
5572 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5573 source,
5574 Handle<Context>(Top::context()),
5575 Top::context()->IsGlobalContext(),
5576 Compiler::DONT_VALIDATE_JSON);
5577 if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
5578 callee = Factory::NewFunctionFromBoilerplate(
5579 boilerplate,
5580 Handle<Context>(Top::context()),
5581 NOT_TENURED);
5582 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005583}
5584
5585
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5587 // This utility adjusts the property attributes for newly created Function
5588 // object ("new Function(...)") by changing the map.
5589 // All it does is changing the prototype property to enumerable
5590 // as specified in ECMA262, 15.3.5.2.
5591 HandleScope scope;
5592 ASSERT(args.length() == 1);
5593 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5594 ASSERT(func->map()->instance_type() ==
5595 Top::function_instance_map()->instance_type());
5596 ASSERT(func->map()->instance_size() ==
5597 Top::function_instance_map()->instance_size());
5598 func->set_map(*Top::function_instance_map());
5599 return *func;
5600}
5601
5602
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005603// Push an array unto an array of arrays if it is not already in the
5604// array. Returns true if the element was pushed on the stack and
5605// false otherwise.
5606static Object* Runtime_PushIfAbsent(Arguments args) {
5607 ASSERT(args.length() == 2);
5608 CONVERT_CHECKED(JSArray, array, args[0]);
5609 CONVERT_CHECKED(JSArray, element, args[1]);
5610 RUNTIME_ASSERT(array->HasFastElements());
5611 int length = Smi::cast(array->length())->value();
5612 FixedArray* elements = FixedArray::cast(array->elements());
5613 for (int i = 0; i < length; i++) {
5614 if (elements->get(i) == element) return Heap::false_value();
5615 }
5616 Object* obj = array->SetFastElement(length, element);
5617 if (obj->IsFailure()) return obj;
5618 return Heap::true_value();
5619}
5620
5621
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005622/**
5623 * A simple visitor visits every element of Array's.
5624 * The backend storage can be a fixed array for fast elements case,
5625 * or a dictionary for sparse array. Since Dictionary is a subtype
5626 * of FixedArray, the class can be used by both fast and slow cases.
5627 * The second parameter of the constructor, fast_elements, specifies
5628 * whether the storage is a FixedArray or Dictionary.
5629 *
5630 * An index limit is used to deal with the situation that a result array
5631 * length overflows 32-bit non-negative integer.
5632 */
5633class ArrayConcatVisitor {
5634 public:
5635 ArrayConcatVisitor(Handle<FixedArray> storage,
5636 uint32_t index_limit,
5637 bool fast_elements) :
5638 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005639 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005640
5641 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005642 if (i >= index_limit_ - index_offset_) return;
5643 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005644
5645 if (fast_elements_) {
5646 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5647 storage_->set(index, *elm);
5648
5649 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005650 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5651 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005652 Factory::DictionaryAtNumberPut(dict, index, elm);
5653 if (!result.is_identical_to(dict))
5654 storage_ = result;
5655 }
5656 }
5657
5658 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005659 if (index_limit_ - index_offset_ < delta) {
5660 index_offset_ = index_limit_;
5661 } else {
5662 index_offset_ += delta;
5663 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005664 }
5665
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00005666 Handle<FixedArray> storage() { return storage_; }
5667
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005668 private:
5669 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005670 // Limit on the accepted indices. Elements with indices larger than the
5671 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005672 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005673 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005674 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005675 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005676};
5677
5678
ager@chromium.org3811b432009-10-28 14:53:37 +00005679template<class ExternalArrayClass, class ElementType>
5680static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5681 bool elements_are_ints,
5682 bool elements_are_guaranteed_smis,
5683 uint32_t range,
5684 ArrayConcatVisitor* visitor) {
5685 Handle<ExternalArrayClass> array(
5686 ExternalArrayClass::cast(receiver->elements()));
5687 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5688
5689 if (visitor != NULL) {
5690 if (elements_are_ints) {
5691 if (elements_are_guaranteed_smis) {
5692 for (uint32_t j = 0; j < len; j++) {
5693 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5694 visitor->visit(j, e);
5695 }
5696 } else {
5697 for (uint32_t j = 0; j < len; j++) {
5698 int64_t val = static_cast<int64_t>(array->get(j));
5699 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5700 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5701 visitor->visit(j, e);
5702 } else {
5703 Handle<Object> e(
5704 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5705 visitor->visit(j, e);
5706 }
5707 }
5708 }
5709 } else {
5710 for (uint32_t j = 0; j < len; j++) {
5711 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5712 visitor->visit(j, e);
5713 }
5714 }
5715 }
5716
5717 return len;
5718}
5719
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005720/**
5721 * A helper function that visits elements of a JSObject. Only elements
5722 * whose index between 0 and range (exclusive) are visited.
5723 *
5724 * If the third parameter, visitor, is not NULL, the visitor is called
5725 * with parameters, 'visitor_index_offset + element index' and the element.
5726 *
5727 * It returns the number of visisted elements.
5728 */
5729static uint32_t IterateElements(Handle<JSObject> receiver,
5730 uint32_t range,
5731 ArrayConcatVisitor* visitor) {
5732 uint32_t num_of_elements = 0;
5733
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005734 switch (receiver->GetElementsKind()) {
5735 case JSObject::FAST_ELEMENTS: {
5736 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5737 uint32_t len = elements->length();
5738 if (range < len) {
5739 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005740 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005741
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005742 for (uint32_t j = 0; j < len; j++) {
5743 Handle<Object> e(elements->get(j));
5744 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005745 num_of_elements++;
5746 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005747 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005748 }
5749 }
5750 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005751 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005752 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005753 case JSObject::PIXEL_ELEMENTS: {
5754 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5755 uint32_t len = pixels->length();
5756 if (range < len) {
5757 len = range;
5758 }
5759
5760 for (uint32_t j = 0; j < len; j++) {
5761 num_of_elements++;
5762 if (visitor != NULL) {
5763 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5764 visitor->visit(j, e);
5765 }
5766 }
5767 break;
5768 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005769 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5770 num_of_elements =
5771 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5772 receiver, true, true, range, visitor);
5773 break;
5774 }
5775 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5776 num_of_elements =
5777 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5778 receiver, true, true, range, visitor);
5779 break;
5780 }
5781 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5782 num_of_elements =
5783 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5784 receiver, true, true, range, visitor);
5785 break;
5786 }
5787 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5788 num_of_elements =
5789 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5790 receiver, true, true, range, visitor);
5791 break;
5792 }
5793 case JSObject::EXTERNAL_INT_ELEMENTS: {
5794 num_of_elements =
5795 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5796 receiver, true, false, range, visitor);
5797 break;
5798 }
5799 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5800 num_of_elements =
5801 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5802 receiver, true, false, range, visitor);
5803 break;
5804 }
5805 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5806 num_of_elements =
5807 IterateExternalArrayElements<ExternalFloatArray, float>(
5808 receiver, false, false, range, visitor);
5809 break;
5810 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005811 case JSObject::DICTIONARY_ELEMENTS: {
5812 Handle<NumberDictionary> dict(receiver->element_dictionary());
5813 uint32_t capacity = dict->Capacity();
5814 for (uint32_t j = 0; j < capacity; j++) {
5815 Handle<Object> k(dict->KeyAt(j));
5816 if (dict->IsKey(*k)) {
5817 ASSERT(k->IsNumber());
5818 uint32_t index = static_cast<uint32_t>(k->Number());
5819 if (index < range) {
5820 num_of_elements++;
5821 if (visitor) {
5822 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5823 }
5824 }
5825 }
5826 }
5827 break;
5828 }
5829 default:
5830 UNREACHABLE();
5831 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005832 }
5833
5834 return num_of_elements;
5835}
5836
5837
5838/**
5839 * A helper function that visits elements of an Array object, and elements
5840 * on its prototypes.
5841 *
5842 * Elements on prototypes are visited first, and only elements whose indices
5843 * less than Array length are visited.
5844 *
5845 * If a ArrayConcatVisitor object is given, the visitor is called with
5846 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005847 *
5848 * The returned number of elements is an upper bound on the actual number
5849 * of elements added. If the same element occurs in more than one object
5850 * in the array's prototype chain, it will be counted more than once, but
5851 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005852 */
5853static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5854 ArrayConcatVisitor* visitor) {
5855 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5856 Handle<Object> obj = array;
5857
5858 static const int kEstimatedPrototypes = 3;
5859 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5860
5861 // Visit prototype first. If an element on the prototype is shadowed by
5862 // the inheritor using the same index, the ArrayConcatVisitor visits
5863 // the prototype element before the shadowing element.
5864 // The visitor can simply overwrite the old value by new value using
5865 // the same index. This follows Array::concat semantics.
5866 while (!obj->IsNull()) {
5867 objects.Add(Handle<JSObject>::cast(obj));
5868 obj = Handle<Object>(obj->GetPrototype());
5869 }
5870
5871 uint32_t nof_elements = 0;
5872 for (int i = objects.length() - 1; i >= 0; i--) {
5873 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005874 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005875 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005876
5877 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
5878 nof_elements = JSObject::kMaxElementCount;
5879 } else {
5880 nof_elements += encountered_elements;
5881 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005882 }
5883
5884 return nof_elements;
5885}
5886
5887
5888/**
5889 * A helper function of Runtime_ArrayConcat.
5890 *
5891 * The first argument is an Array of arrays and objects. It is the
5892 * same as the arguments array of Array::concat JS function.
5893 *
5894 * If an argument is an Array object, the function visits array
5895 * elements. If an argument is not an Array object, the function
5896 * visits the object as if it is an one-element array.
5897 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005898 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005899 * non-negative number is used as new length. For example, if one
5900 * array length is 2^32 - 1, second array length is 1, the
5901 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005902 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
5903 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005904 */
5905static uint32_t IterateArguments(Handle<JSArray> arguments,
5906 ArrayConcatVisitor* visitor) {
5907 uint32_t visited_elements = 0;
5908 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5909
5910 for (uint32_t i = 0; i < num_of_args; i++) {
5911 Handle<Object> obj(arguments->GetElement(i));
5912 if (obj->IsJSArray()) {
5913 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5914 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5915 uint32_t nof_elements =
5916 IterateArrayAndPrototypeElements(array, visitor);
5917 // Total elements of array and its prototype chain can be more than
5918 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005919 // the array length number of elements. We use the length as an estimate
5920 // for the actual number of elements added.
5921 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
5922 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
5923 visited_elements = JSArray::kMaxElementCount;
5924 } else {
5925 visited_elements += added_elements;
5926 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005927 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005928 } else {
5929 if (visitor) {
5930 visitor->visit(0, obj);
5931 visitor->increase_index_offset(1);
5932 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005933 if (visited_elements < JSArray::kMaxElementCount) {
5934 visited_elements++;
5935 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005936 }
5937 }
5938 return visited_elements;
5939}
5940
5941
5942/**
5943 * Array::concat implementation.
5944 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005945 * TODO(lrn): Fix non-compliance for very large concatenations and update to
5946 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005947 */
5948static Object* Runtime_ArrayConcat(Arguments args) {
5949 ASSERT(args.length() == 1);
5950 HandleScope handle_scope;
5951
5952 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5953 Handle<JSArray> arguments(arg_arrays);
5954
5955 // Pass 1: estimate the number of elements of the result
5956 // (it could be more than real numbers if prototype has elements).
5957 uint32_t result_length = 0;
5958 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5959
5960 { AssertNoAllocation nogc;
5961 for (uint32_t i = 0; i < num_of_args; i++) {
5962 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005963 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005964 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005965 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005966 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5967 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005968 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005969 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005970 if (JSObject::kMaxElementCount - result_length < length_estimate) {
5971 result_length = JSObject::kMaxElementCount;
5972 break;
5973 }
5974 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005975 }
5976 }
5977
5978 // Allocate an empty array, will set length and content later.
5979 Handle<JSArray> result = Factory::NewJSArray(0);
5980
5981 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5982 // If estimated number of elements is more than half of length, a
5983 // fixed array (fast case) is more time and space-efficient than a
5984 // dictionary.
5985 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5986
5987 Handle<FixedArray> storage;
5988 if (fast_case) {
5989 // The backing storage array must have non-existing elements to
5990 // preserve holes across concat operations.
5991 storage = Factory::NewFixedArrayWithHoles(result_length);
5992
5993 } else {
5994 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5995 uint32_t at_least_space_for = estimate_nof_elements +
5996 (estimate_nof_elements >> 2);
5997 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005998 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005999 }
6000
6001 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
6002
6003 ArrayConcatVisitor visitor(storage, result_length, fast_case);
6004
6005 IterateArguments(arguments, &visitor);
6006
6007 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00006008 // Please note the storage might have changed in the visitor.
6009 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006010
6011 return *result;
6012}
6013
6014
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006015// This will not allocate (flatten the string), but it may run
6016// very slowly for very deeply nested ConsStrings. For debugging use only.
6017static Object* Runtime_GlobalPrint(Arguments args) {
6018 NoHandleAllocation ha;
6019 ASSERT(args.length() == 1);
6020
6021 CONVERT_CHECKED(String, string, args[0]);
6022 StringInputBuffer buffer(string);
6023 while (buffer.has_more()) {
6024 uint16_t character = buffer.GetNext();
6025 PrintF("%c", character);
6026 }
6027 return string;
6028}
6029
ager@chromium.org5ec48922009-05-05 07:25:34 +00006030// Moves all own elements of an object, that are below a limit, to positions
6031// starting at zero. All undefined values are placed after non-undefined values,
6032// and are followed by non-existing element. Does not change the length
6033// property.
6034// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006035static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00006036 ASSERT(args.length() == 2);
6037 CONVERT_CHECKED(JSObject, object, args[0]);
6038 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
6039 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006040}
6041
6042
6043// Move contents of argument 0 (an array) to argument 1 (an array)
6044static Object* Runtime_MoveArrayContents(Arguments args) {
6045 ASSERT(args.length() == 2);
6046 CONVERT_CHECKED(JSArray, from, args[0]);
6047 CONVERT_CHECKED(JSArray, to, args[1]);
6048 to->SetContent(FixedArray::cast(from->elements()));
6049 to->set_length(from->length());
6050 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006051 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006052 return to;
6053}
6054
6055
6056// How many elements does this array have?
6057static Object* Runtime_EstimateNumberOfElements(Arguments args) {
6058 ASSERT(args.length() == 1);
6059 CONVERT_CHECKED(JSArray, array, args[0]);
6060 HeapObject* elements = array->elements();
6061 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006062 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006063 } else {
6064 return array->length();
6065 }
6066}
6067
6068
6069// Returns an array that tells you where in the [0, length) interval an array
6070// might have elements. Can either return keys or intervals. Keys can have
6071// gaps in (undefined). Intervals can also span over some undefined keys.
6072static Object* Runtime_GetArrayKeys(Arguments args) {
6073 ASSERT(args.length() == 2);
6074 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00006075 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006076 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006077 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006078 // Create an array and get all the keys into it, then remove all the
6079 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006080 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006081 int keys_length = keys->length();
6082 for (int i = 0; i < keys_length; i++) {
6083 Object* key = keys->get(i);
6084 uint32_t index;
6085 if (!Array::IndexFromObject(key, &index) || index >= length) {
6086 // Zap invalid keys.
6087 keys->set_undefined(i);
6088 }
6089 }
6090 return *Factory::NewJSArrayWithElements(keys);
6091 } else {
6092 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
6093 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006094 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00006095 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
6096 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006097 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00006098 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006099 single_interval->set(1, *length_object);
6100 return *Factory::NewJSArrayWithElements(single_interval);
6101 }
6102}
6103
6104
6105// DefineAccessor takes an optional final argument which is the
6106// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
6107// to the way accessors are implemented, it is set for both the getter
6108// and setter on the first call to DefineAccessor and ignored on
6109// subsequent calls.
6110static Object* Runtime_DefineAccessor(Arguments args) {
6111 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
6112 // Compute attributes.
6113 PropertyAttributes attributes = NONE;
6114 if (args.length() == 5) {
6115 CONVERT_CHECKED(Smi, attrs, args[4]);
6116 int value = attrs->value();
6117 // Only attribute bits should be set.
6118 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
6119 attributes = static_cast<PropertyAttributes>(value);
6120 }
6121
6122 CONVERT_CHECKED(JSObject, obj, args[0]);
6123 CONVERT_CHECKED(String, name, args[1]);
6124 CONVERT_CHECKED(Smi, flag, args[2]);
6125 CONVERT_CHECKED(JSFunction, fun, args[3]);
6126 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
6127}
6128
6129
6130static Object* Runtime_LookupAccessor(Arguments args) {
6131 ASSERT(args.length() == 3);
6132 CONVERT_CHECKED(JSObject, obj, args[0]);
6133 CONVERT_CHECKED(String, name, args[1]);
6134 CONVERT_CHECKED(Smi, flag, args[2]);
6135 return obj->LookupAccessor(name, flag->value() == 0);
6136}
6137
6138
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006139#ifdef ENABLE_DEBUGGER_SUPPORT
6140static Object* Runtime_DebugBreak(Arguments args) {
6141 ASSERT(args.length() == 0);
6142 return Execution::DebugBreakHelper();
6143}
6144
6145
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006146// Helper functions for wrapping and unwrapping stack frame ids.
6147static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006148 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006149 return Smi::FromInt(id >> 2);
6150}
6151
6152
6153static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
6154 return static_cast<StackFrame::Id>(wrapped->value() << 2);
6155}
6156
6157
6158// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00006159// args[0]: debug event listener function to set or null or undefined for
6160// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006161// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00006162static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006163 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006164 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
6165 args[0]->IsUndefined() ||
6166 args[0]->IsNull());
6167 Handle<Object> callback = args.at<Object>(0);
6168 Handle<Object> data = args.at<Object>(1);
6169 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006170
6171 return Heap::undefined_value();
6172}
6173
6174
6175static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006176 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006177 StackGuard::DebugBreak();
6178 return Heap::undefined_value();
6179}
6180
6181
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006182static Object* DebugLookupResultValue(Object* receiver, String* name,
6183 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00006184 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006185 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006186 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006187 case NORMAL:
6188 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006189 if (value->IsTheHole()) {
6190 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006191 }
6192 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006193 case FIELD:
6194 value =
6195 JSObject::cast(
6196 result->holder())->FastPropertyAt(result->GetFieldIndex());
6197 if (value->IsTheHole()) {
6198 return Heap::undefined_value();
6199 }
6200 return value;
6201 case CONSTANT_FUNCTION:
6202 return result->GetConstantFunction();
6203 case CALLBACKS: {
6204 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006205 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006206 value = receiver->GetPropertyWithCallback(
6207 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00006208 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00006209 value = Top::pending_exception();
6210 Top::clear_pending_exception();
6211 if (caught_exception != NULL) {
6212 *caught_exception = true;
6213 }
6214 }
6215 return value;
6216 } else {
6217 return Heap::undefined_value();
6218 }
6219 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006220 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006221 case MAP_TRANSITION:
6222 case CONSTANT_TRANSITION:
6223 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006224 return Heap::undefined_value();
6225 default:
6226 UNREACHABLE();
6227 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006228 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006229 return Heap::undefined_value();
6230}
6231
6232
ager@chromium.org32912102009-01-16 10:38:43 +00006233// Get debugger related details for an object property.
6234// args[0]: object holding property
6235// args[1]: name of the property
6236//
6237// The array returned contains the following information:
6238// 0: Property value
6239// 1: Property details
6240// 2: Property value is exception
6241// 3: Getter function if defined
6242// 4: Setter function if defined
6243// Items 2-4 are only filled if the property has either a getter or a setter
6244// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006245static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006246 HandleScope scope;
6247
6248 ASSERT(args.length() == 2);
6249
6250 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6251 CONVERT_ARG_CHECKED(String, name, 1);
6252
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00006253 // Make sure to set the current context to the context before the debugger was
6254 // entered (if the debugger is entered). The reason for switching context here
6255 // is that for some property lookups (accessors and interceptors) callbacks
6256 // into the embedding application can occour, and the embedding application
6257 // could have the assumption that its own global context is the current
6258 // context and not some internal debugger context.
6259 SaveContext save;
6260 if (Debug::InDebugger()) {
6261 Top::set_context(*Debug::debugger_entry()->GetContext());
6262 }
6263
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006264 // Skip the global proxy as it has no properties and always delegates to the
6265 // real global object.
6266 if (obj->IsJSGlobalProxy()) {
6267 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6268 }
6269
6270
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006271 // Check if the name is trivially convertible to an index and get the element
6272 // if so.
6273 uint32_t index;
6274 if (name->AsArrayIndex(&index)) {
6275 Handle<FixedArray> details = Factory::NewFixedArray(2);
6276 details->set(0, Runtime::GetElementOrCharAt(obj, index));
6277 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
6278 return *Factory::NewJSArrayWithElements(details);
6279 }
6280
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006281 // Find the number of objects making up this.
6282 int length = LocalPrototypeChainLength(*obj);
6283
6284 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006285 Handle<JSObject> jsproto = obj;
6286 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006287 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006288 jsproto->LocalLookup(*name, &result);
6289 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006290 // LookupResult is not GC safe as it holds raw object pointers.
6291 // GC can happen later in this code so put the required fields into
6292 // local variables using handles when required for later use.
6293 PropertyType result_type = result.type();
6294 Handle<Object> result_callback_obj;
6295 if (result_type == CALLBACKS) {
6296 result_callback_obj = Handle<Object>(result.GetCallbackObject());
6297 }
6298 Smi* property_details = result.GetPropertyDetails().AsSmi();
6299 // DebugLookupResultValue can cause GC so details from LookupResult needs
6300 // to be copied to handles before this.
6301 bool caught_exception = false;
6302 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
6303 &caught_exception);
6304 if (raw_value->IsFailure()) return raw_value;
6305 Handle<Object> value(raw_value);
6306
6307 // If the callback object is a fixed array then it contains JavaScript
6308 // getter and/or setter.
6309 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
6310 result_callback_obj->IsFixedArray();
6311 Handle<FixedArray> details =
6312 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
6313 details->set(0, *value);
6314 details->set(1, property_details);
6315 if (hasJavaScriptAccessors) {
6316 details->set(2,
6317 caught_exception ? Heap::true_value()
6318 : Heap::false_value());
6319 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
6320 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
6321 }
6322
6323 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006324 }
6325 if (i < length - 1) {
6326 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6327 }
6328 }
6329
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006330 return Heap::undefined_value();
6331}
6332
6333
6334static Object* Runtime_DebugGetProperty(Arguments args) {
6335 HandleScope scope;
6336
6337 ASSERT(args.length() == 2);
6338
6339 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6340 CONVERT_ARG_CHECKED(String, name, 1);
6341
6342 LookupResult result;
6343 obj->Lookup(*name, &result);
6344 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006345 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006346 }
6347 return Heap::undefined_value();
6348}
6349
6350
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006351// Return the property type calculated from the property details.
6352// args[0]: smi with property details.
6353static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6354 ASSERT(args.length() == 1);
6355 CONVERT_CHECKED(Smi, details, args[0]);
6356 PropertyType type = PropertyDetails(details).type();
6357 return Smi::FromInt(static_cast<int>(type));
6358}
6359
6360
6361// Return the property attribute calculated from the property details.
6362// args[0]: smi with property details.
6363static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6364 ASSERT(args.length() == 1);
6365 CONVERT_CHECKED(Smi, details, args[0]);
6366 PropertyAttributes attributes = PropertyDetails(details).attributes();
6367 return Smi::FromInt(static_cast<int>(attributes));
6368}
6369
6370
6371// Return the property insertion index calculated from the property details.
6372// args[0]: smi with property details.
6373static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6374 ASSERT(args.length() == 1);
6375 CONVERT_CHECKED(Smi, details, args[0]);
6376 int index = PropertyDetails(details).index();
6377 return Smi::FromInt(index);
6378}
6379
6380
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006381// Return property value from named interceptor.
6382// args[0]: object
6383// args[1]: property name
6384static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6385 HandleScope scope;
6386 ASSERT(args.length() == 2);
6387 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6388 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6389 CONVERT_ARG_CHECKED(String, name, 1);
6390
6391 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006392 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006393}
6394
6395
6396// Return element value from indexed interceptor.
6397// args[0]: object
6398// args[1]: index
6399static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6400 HandleScope scope;
6401 ASSERT(args.length() == 2);
6402 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6403 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6404 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6405
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006406 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006407}
6408
6409
6410static Object* Runtime_CheckExecutionState(Arguments args) {
6411 ASSERT(args.length() >= 1);
6412 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006413 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006414 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006415 return Top::Throw(Heap::illegal_execution_state_symbol());
6416 }
6417
6418 return Heap::true_value();
6419}
6420
6421
6422static Object* Runtime_GetFrameCount(Arguments args) {
6423 HandleScope scope;
6424 ASSERT(args.length() == 1);
6425
6426 // Check arguments.
6427 Object* result = Runtime_CheckExecutionState(args);
6428 if (result->IsFailure()) return result;
6429
6430 // Count all frames which are relevant to debugging stack trace.
6431 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006432 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006433 if (id == StackFrame::NO_ID) {
6434 // If there is no JavaScript stack frame count is 0.
6435 return Smi::FromInt(0);
6436 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006437 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6438 return Smi::FromInt(n);
6439}
6440
6441
6442static const int kFrameDetailsFrameIdIndex = 0;
6443static const int kFrameDetailsReceiverIndex = 1;
6444static const int kFrameDetailsFunctionIndex = 2;
6445static const int kFrameDetailsArgumentCountIndex = 3;
6446static const int kFrameDetailsLocalCountIndex = 4;
6447static const int kFrameDetailsSourcePositionIndex = 5;
6448static const int kFrameDetailsConstructCallIndex = 6;
6449static const int kFrameDetailsDebuggerFrameIndex = 7;
6450static const int kFrameDetailsFirstDynamicIndex = 8;
6451
6452// Return an array with frame details
6453// args[0]: number: break id
6454// args[1]: number: frame index
6455//
6456// The array returned contains the following information:
6457// 0: Frame id
6458// 1: Receiver
6459// 2: Function
6460// 3: Argument count
6461// 4: Local count
6462// 5: Source position
6463// 6: Constructor call
6464// 7: Debugger frame
6465// Arguments name, value
6466// Locals name, value
6467static Object* Runtime_GetFrameDetails(Arguments args) {
6468 HandleScope scope;
6469 ASSERT(args.length() == 2);
6470
6471 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006472 Object* check = Runtime_CheckExecutionState(args);
6473 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006474 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6475
6476 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006477 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006478 if (id == StackFrame::NO_ID) {
6479 // If there are no JavaScript stack frames return undefined.
6480 return Heap::undefined_value();
6481 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006482 int count = 0;
6483 JavaScriptFrameIterator it(id);
6484 for (; !it.done(); it.Advance()) {
6485 if (count == index) break;
6486 count++;
6487 }
6488 if (it.done()) return Heap::undefined_value();
6489
6490 // Traverse the saved contexts chain to find the active context for the
6491 // selected frame.
6492 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006493 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006494 save = save->prev();
6495 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006496 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006497
6498 // Get the frame id.
6499 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6500
6501 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006502 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006503
6504 // Check for constructor frame.
6505 bool constructor = it.frame()->IsConstructor();
6506
6507 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006508 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006509 ScopeInfo<> info(*code);
6510
6511 // Get the context.
6512 Handle<Context> context(Context::cast(it.frame()->context()));
6513
6514 // Get the locals names and values into a temporary array.
6515 //
6516 // TODO(1240907): Hide compiler-introduced stack variables
6517 // (e.g. .result)? For users of the debugger, they will probably be
6518 // confusing.
6519 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6520 for (int i = 0; i < info.NumberOfLocals(); i++) {
6521 // Name of the local.
6522 locals->set(i * 2, *info.LocalName(i));
6523
6524 // Fetch the value of the local - either from the stack or from a
6525 // heap-allocated context.
6526 if (i < info.number_of_stack_slots()) {
6527 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6528 } else {
6529 Handle<String> name = info.LocalName(i);
6530 // Traverse the context chain to the function context as all local
6531 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006532 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006533 context = Handle<Context>(context->previous());
6534 }
6535 ASSERT(context->is_function_context());
6536 locals->set(i * 2 + 1,
6537 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6538 NULL)));
6539 }
6540 }
6541
6542 // Now advance to the arguments adapter frame (if any). If contains all
6543 // the provided parameters and
6544
6545 // Now advance to the arguments adapter frame (if any). It contains all
6546 // the provided parameters whereas the function frame always have the number
6547 // of arguments matching the functions parameters. The rest of the
6548 // information (except for what is collected above) is the same.
6549 it.AdvanceToArgumentsFrame();
6550
6551 // Find the number of arguments to fill. At least fill the number of
6552 // parameters for the function and fill more if more parameters are provided.
6553 int argument_count = info.number_of_parameters();
6554 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6555 argument_count = it.frame()->GetProvidedParametersCount();
6556 }
6557
6558 // Calculate the size of the result.
6559 int details_size = kFrameDetailsFirstDynamicIndex +
6560 2 * (argument_count + info.NumberOfLocals());
6561 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6562
6563 // Add the frame id.
6564 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6565
6566 // Add the function (same as in function frame).
6567 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6568
6569 // Add the arguments count.
6570 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6571
6572 // Add the locals count
6573 details->set(kFrameDetailsLocalCountIndex,
6574 Smi::FromInt(info.NumberOfLocals()));
6575
6576 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006577 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006578 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6579 } else {
6580 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6581 }
6582
6583 // Add the constructor information.
6584 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6585
6586 // Add information on whether this frame is invoked in the debugger context.
6587 details->set(kFrameDetailsDebuggerFrameIndex,
6588 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6589
6590 // Fill the dynamic part.
6591 int details_index = kFrameDetailsFirstDynamicIndex;
6592
6593 // Add arguments name and value.
6594 for (int i = 0; i < argument_count; i++) {
6595 // Name of the argument.
6596 if (i < info.number_of_parameters()) {
6597 details->set(details_index++, *info.parameter_name(i));
6598 } else {
6599 details->set(details_index++, Heap::undefined_value());
6600 }
6601
6602 // Parameter value.
6603 if (i < it.frame()->GetProvidedParametersCount()) {
6604 details->set(details_index++, it.frame()->GetParameter(i));
6605 } else {
6606 details->set(details_index++, Heap::undefined_value());
6607 }
6608 }
6609
6610 // Add locals name and value from the temporary copy from the function frame.
6611 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6612 details->set(details_index++, locals->get(i));
6613 }
6614
6615 // Add the receiver (same as in function frame).
6616 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6617 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6618 Handle<Object> receiver(it.frame()->receiver());
6619 if (!receiver->IsJSObject()) {
6620 // If the receiver is NOT a JSObject we have hit an optimization
6621 // where a value object is not converted into a wrapped JS objects.
6622 // To hide this optimization from the debugger, we wrap the receiver
6623 // by creating correct wrapper object based on the calling frame's
6624 // global context.
6625 it.Advance();
6626 Handle<Context> calling_frames_global_context(
6627 Context::cast(Context::cast(it.frame()->context())->global_context()));
6628 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6629 }
6630 details->set(kFrameDetailsReceiverIndex, *receiver);
6631
6632 ASSERT_EQ(details_size, details_index);
6633 return *Factory::NewJSArrayWithElements(details);
6634}
6635
6636
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006637// Copy all the context locals into an object used to materialize a scope.
6638static void CopyContextLocalsToScopeObject(Handle<Code> code,
6639 ScopeInfo<>& scope_info,
6640 Handle<Context> context,
6641 Handle<JSObject> scope_object) {
6642 // Fill all context locals to the context extension.
6643 for (int i = Context::MIN_CONTEXT_SLOTS;
6644 i < scope_info.number_of_context_slots();
6645 i++) {
6646 int context_index =
6647 ScopeInfo<>::ContextSlotIndex(*code,
6648 *scope_info.context_slot_name(i),
6649 NULL);
6650
6651 // Don't include the arguments shadow (.arguments) context variable.
6652 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6653 SetProperty(scope_object,
6654 scope_info.context_slot_name(i),
6655 Handle<Object>(context->get(context_index)), NONE);
6656 }
6657 }
6658}
6659
6660
6661// Create a plain JSObject which materializes the local scope for the specified
6662// frame.
6663static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6664 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6665 Handle<Code> code(function->code());
6666 ScopeInfo<> scope_info(*code);
6667
6668 // Allocate and initialize a JSObject with all the arguments, stack locals
6669 // heap locals and extension properties of the debugged function.
6670 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6671
6672 // First fill all parameters.
6673 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6674 SetProperty(local_scope,
6675 scope_info.parameter_name(i),
6676 Handle<Object>(frame->GetParameter(i)), NONE);
6677 }
6678
6679 // Second fill all stack locals.
6680 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6681 SetProperty(local_scope,
6682 scope_info.stack_slot_name(i),
6683 Handle<Object>(frame->GetExpression(i)), NONE);
6684 }
6685
6686 // Third fill all context locals.
6687 Handle<Context> frame_context(Context::cast(frame->context()));
6688 Handle<Context> function_context(frame_context->fcontext());
6689 CopyContextLocalsToScopeObject(code, scope_info,
6690 function_context, local_scope);
6691
6692 // Finally copy any properties from the function context extension. This will
6693 // be variables introduced by eval.
6694 if (function_context->closure() == *function) {
6695 if (function_context->has_extension() &&
6696 !function_context->IsGlobalContext()) {
6697 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006698 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006699 for (int i = 0; i < keys->length(); i++) {
6700 // Names of variables introduced by eval are strings.
6701 ASSERT(keys->get(i)->IsString());
6702 Handle<String> key(String::cast(keys->get(i)));
6703 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6704 }
6705 }
6706 }
6707 return local_scope;
6708}
6709
6710
6711// Create a plain JSObject which materializes the closure content for the
6712// context.
6713static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6714 ASSERT(context->is_function_context());
6715
6716 Handle<Code> code(context->closure()->code());
6717 ScopeInfo<> scope_info(*code);
6718
6719 // Allocate and initialize a JSObject with all the content of theis function
6720 // closure.
6721 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6722
6723 // Check whether the arguments shadow object exists.
6724 int arguments_shadow_index =
6725 ScopeInfo<>::ContextSlotIndex(*code,
6726 Heap::arguments_shadow_symbol(),
6727 NULL);
6728 if (arguments_shadow_index >= 0) {
6729 // In this case all the arguments are available in the arguments shadow
6730 // object.
6731 Handle<JSObject> arguments_shadow(
6732 JSObject::cast(context->get(arguments_shadow_index)));
6733 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6734 SetProperty(closure_scope,
6735 scope_info.parameter_name(i),
6736 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6737 }
6738 }
6739
6740 // Fill all context locals to the context extension.
6741 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6742
6743 // Finally copy any properties from the function context extension. This will
6744 // be variables introduced by eval.
6745 if (context->has_extension()) {
6746 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006747 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006748 for (int i = 0; i < keys->length(); i++) {
6749 // Names of variables introduced by eval are strings.
6750 ASSERT(keys->get(i)->IsString());
6751 Handle<String> key(String::cast(keys->get(i)));
6752 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6753 }
6754 }
6755
6756 return closure_scope;
6757}
6758
6759
6760// Iterate over the actual scopes visible from a stack frame. All scopes are
6761// backed by an actual context except the local scope, which is inserted
6762// "artifically" in the context chain.
6763class ScopeIterator {
6764 public:
6765 enum ScopeType {
6766 ScopeTypeGlobal = 0,
6767 ScopeTypeLocal,
6768 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006769 ScopeTypeClosure,
6770 // Every catch block contains an implicit with block (its parameter is
6771 // a JSContextExtensionObject) that extends current scope with a variable
6772 // holding exception object. Such with blocks are treated as scopes of their
6773 // own type.
6774 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006775 };
6776
6777 explicit ScopeIterator(JavaScriptFrame* frame)
6778 : frame_(frame),
6779 function_(JSFunction::cast(frame->function())),
6780 context_(Context::cast(frame->context())),
6781 local_done_(false),
6782 at_local_(false) {
6783
6784 // Check whether the first scope is actually a local scope.
6785 if (context_->IsGlobalContext()) {
6786 // If there is a stack slot for .result then this local scope has been
6787 // created for evaluating top level code and it is not a real local scope.
6788 // Checking for the existence of .result seems fragile, but the scope info
6789 // saved with the code object does not otherwise have that information.
6790 Handle<Code> code(function_->code());
6791 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6792 at_local_ = index < 0;
6793 } else if (context_->is_function_context()) {
6794 at_local_ = true;
6795 }
6796 }
6797
6798 // More scopes?
6799 bool Done() { return context_.is_null(); }
6800
6801 // Move to the next scope.
6802 void Next() {
6803 // If at a local scope mark the local scope as passed.
6804 if (at_local_) {
6805 at_local_ = false;
6806 local_done_ = true;
6807
6808 // If the current context is not associated with the local scope the
6809 // current context is the next real scope, so don't move to the next
6810 // context in this case.
6811 if (context_->closure() != *function_) {
6812 return;
6813 }
6814 }
6815
6816 // The global scope is always the last in the chain.
6817 if (context_->IsGlobalContext()) {
6818 context_ = Handle<Context>();
6819 return;
6820 }
6821
6822 // Move to the next context.
6823 if (context_->is_function_context()) {
6824 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6825 } else {
6826 context_ = Handle<Context>(context_->previous());
6827 }
6828
6829 // If passing the local scope indicate that the current scope is now the
6830 // local scope.
6831 if (!local_done_ &&
6832 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6833 at_local_ = true;
6834 }
6835 }
6836
6837 // Return the type of the current scope.
6838 int Type() {
6839 if (at_local_) {
6840 return ScopeTypeLocal;
6841 }
6842 if (context_->IsGlobalContext()) {
6843 ASSERT(context_->global()->IsGlobalObject());
6844 return ScopeTypeGlobal;
6845 }
6846 if (context_->is_function_context()) {
6847 return ScopeTypeClosure;
6848 }
6849 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006850 // Current scope is either an explicit with statement or a with statement
6851 // implicitely generated for a catch block.
6852 // If the extension object here is a JSContextExtensionObject then
6853 // current with statement is one frome a catch block otherwise it's a
6854 // regular with statement.
6855 if (context_->extension()->IsJSContextExtensionObject()) {
6856 return ScopeTypeCatch;
6857 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006858 return ScopeTypeWith;
6859 }
6860
6861 // Return the JavaScript object with the content of the current scope.
6862 Handle<JSObject> ScopeObject() {
6863 switch (Type()) {
6864 case ScopeIterator::ScopeTypeGlobal:
6865 return Handle<JSObject>(CurrentContext()->global());
6866 break;
6867 case ScopeIterator::ScopeTypeLocal:
6868 // Materialize the content of the local scope into a JSObject.
6869 return MaterializeLocalScope(frame_);
6870 break;
6871 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006872 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006873 // Return the with object.
6874 return Handle<JSObject>(CurrentContext()->extension());
6875 break;
6876 case ScopeIterator::ScopeTypeClosure:
6877 // Materialize the content of the closure scope into a JSObject.
6878 return MaterializeClosure(CurrentContext());
6879 break;
6880 }
6881 UNREACHABLE();
6882 return Handle<JSObject>();
6883 }
6884
6885 // Return the context for this scope. For the local context there might not
6886 // be an actual context.
6887 Handle<Context> CurrentContext() {
6888 if (at_local_ && context_->closure() != *function_) {
6889 return Handle<Context>();
6890 }
6891 return context_;
6892 }
6893
6894#ifdef DEBUG
6895 // Debug print of the content of the current scope.
6896 void DebugPrint() {
6897 switch (Type()) {
6898 case ScopeIterator::ScopeTypeGlobal:
6899 PrintF("Global:\n");
6900 CurrentContext()->Print();
6901 break;
6902
6903 case ScopeIterator::ScopeTypeLocal: {
6904 PrintF("Local:\n");
6905 Handle<Code> code(function_->code());
6906 ScopeInfo<> scope_info(*code);
6907 scope_info.Print();
6908 if (!CurrentContext().is_null()) {
6909 CurrentContext()->Print();
6910 if (CurrentContext()->has_extension()) {
6911 Handle<JSObject> extension =
6912 Handle<JSObject>(CurrentContext()->extension());
6913 if (extension->IsJSContextExtensionObject()) {
6914 extension->Print();
6915 }
6916 }
6917 }
6918 break;
6919 }
6920
6921 case ScopeIterator::ScopeTypeWith: {
6922 PrintF("With:\n");
6923 Handle<JSObject> extension =
6924 Handle<JSObject>(CurrentContext()->extension());
6925 extension->Print();
6926 break;
6927 }
6928
ager@chromium.orga1645e22009-09-09 19:27:10 +00006929 case ScopeIterator::ScopeTypeCatch: {
6930 PrintF("Catch:\n");
6931 Handle<JSObject> extension =
6932 Handle<JSObject>(CurrentContext()->extension());
6933 extension->Print();
6934 break;
6935 }
6936
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006937 case ScopeIterator::ScopeTypeClosure: {
6938 PrintF("Closure:\n");
6939 CurrentContext()->Print();
6940 if (CurrentContext()->has_extension()) {
6941 Handle<JSObject> extension =
6942 Handle<JSObject>(CurrentContext()->extension());
6943 if (extension->IsJSContextExtensionObject()) {
6944 extension->Print();
6945 }
6946 }
6947 break;
6948 }
6949
6950 default:
6951 UNREACHABLE();
6952 }
6953 PrintF("\n");
6954 }
6955#endif
6956
6957 private:
6958 JavaScriptFrame* frame_;
6959 Handle<JSFunction> function_;
6960 Handle<Context> context_;
6961 bool local_done_;
6962 bool at_local_;
6963
6964 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6965};
6966
6967
6968static Object* Runtime_GetScopeCount(Arguments args) {
6969 HandleScope scope;
6970 ASSERT(args.length() == 2);
6971
6972 // Check arguments.
6973 Object* check = Runtime_CheckExecutionState(args);
6974 if (check->IsFailure()) return check;
6975 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6976
6977 // Get the frame where the debugging is performed.
6978 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6979 JavaScriptFrameIterator it(id);
6980 JavaScriptFrame* frame = it.frame();
6981
6982 // Count the visible scopes.
6983 int n = 0;
6984 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6985 n++;
6986 }
6987
6988 return Smi::FromInt(n);
6989}
6990
6991
6992static const int kScopeDetailsTypeIndex = 0;
6993static const int kScopeDetailsObjectIndex = 1;
6994static const int kScopeDetailsSize = 2;
6995
6996// Return an array with scope details
6997// args[0]: number: break id
6998// args[1]: number: frame index
6999// args[2]: number: scope index
7000//
7001// The array returned contains the following information:
7002// 0: Scope type
7003// 1: Scope object
7004static Object* Runtime_GetScopeDetails(Arguments args) {
7005 HandleScope scope;
7006 ASSERT(args.length() == 3);
7007
7008 // Check arguments.
7009 Object* check = Runtime_CheckExecutionState(args);
7010 if (check->IsFailure()) return check;
7011 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7012 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
7013
7014 // Get the frame where the debugging is performed.
7015 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7016 JavaScriptFrameIterator frame_it(id);
7017 JavaScriptFrame* frame = frame_it.frame();
7018
7019 // Find the requested scope.
7020 int n = 0;
7021 ScopeIterator it(frame);
7022 for (; !it.Done() && n < index; it.Next()) {
7023 n++;
7024 }
7025 if (it.Done()) {
7026 return Heap::undefined_value();
7027 }
7028
7029 // Calculate the size of the result.
7030 int details_size = kScopeDetailsSize;
7031 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
7032
7033 // Fill in scope details.
7034 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
7035 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
7036
7037 return *Factory::NewJSArrayWithElements(details);
7038}
7039
7040
7041static Object* Runtime_DebugPrintScopes(Arguments args) {
7042 HandleScope scope;
7043 ASSERT(args.length() == 0);
7044
7045#ifdef DEBUG
7046 // Print the scopes for the top frame.
7047 StackFrameLocator locator;
7048 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
7049 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
7050 it.DebugPrint();
7051 }
7052#endif
7053 return Heap::undefined_value();
7054}
7055
7056
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007057static Object* Runtime_GetCFrames(Arguments args) {
7058 HandleScope scope;
7059 ASSERT(args.length() == 1);
7060 Object* result = Runtime_CheckExecutionState(args);
7061 if (result->IsFailure()) return result;
7062
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007063#if V8_HOST_ARCH_64_BIT
7064 UNIMPLEMENTED();
7065 return Heap::undefined_value();
7066#else
7067
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007068 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007069 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
7070 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007071 if (frames_count == OS::kStackWalkError) {
7072 return Heap::undefined_value();
7073 }
7074
7075 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
7076 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
7077 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
7078 for (int i = 0; i < frames_count; i++) {
7079 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
7080 frame_value->SetProperty(
7081 *address_str,
7082 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
7083 NONE);
7084
7085 // Get the stack walk text for this frame.
7086 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007087 int frame_text_length = StrLength(frames[i].text);
7088 if (frame_text_length > 0) {
7089 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007090 frame_text = Factory::NewStringFromAscii(str);
7091 }
7092
7093 if (!frame_text.is_null()) {
7094 frame_value->SetProperty(*text_str, *frame_text, NONE);
7095 }
7096
7097 frames_array->set(i, *frame_value);
7098 }
7099 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007100#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007101}
7102
7103
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007104static Object* Runtime_GetThreadCount(Arguments args) {
7105 HandleScope scope;
7106 ASSERT(args.length() == 1);
7107
7108 // Check arguments.
7109 Object* result = Runtime_CheckExecutionState(args);
7110 if (result->IsFailure()) return result;
7111
7112 // Count all archived V8 threads.
7113 int n = 0;
7114 for (ThreadState* thread = ThreadState::FirstInUse();
7115 thread != NULL;
7116 thread = thread->Next()) {
7117 n++;
7118 }
7119
7120 // Total number of threads is current thread and archived threads.
7121 return Smi::FromInt(n + 1);
7122}
7123
7124
7125static const int kThreadDetailsCurrentThreadIndex = 0;
7126static const int kThreadDetailsThreadIdIndex = 1;
7127static const int kThreadDetailsSize = 2;
7128
7129// Return an array with thread details
7130// args[0]: number: break id
7131// args[1]: number: thread index
7132//
7133// The array returned contains the following information:
7134// 0: Is current thread?
7135// 1: Thread id
7136static Object* Runtime_GetThreadDetails(Arguments args) {
7137 HandleScope scope;
7138 ASSERT(args.length() == 2);
7139
7140 // Check arguments.
7141 Object* check = Runtime_CheckExecutionState(args);
7142 if (check->IsFailure()) return check;
7143 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
7144
7145 // Allocate array for result.
7146 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
7147
7148 // Thread index 0 is current thread.
7149 if (index == 0) {
7150 // Fill the details.
7151 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
7152 details->set(kThreadDetailsThreadIdIndex,
7153 Smi::FromInt(ThreadManager::CurrentId()));
7154 } else {
7155 // Find the thread with the requested index.
7156 int n = 1;
7157 ThreadState* thread = ThreadState::FirstInUse();
7158 while (index != n && thread != NULL) {
7159 thread = thread->Next();
7160 n++;
7161 }
7162 if (thread == NULL) {
7163 return Heap::undefined_value();
7164 }
7165
7166 // Fill the details.
7167 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
7168 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
7169 }
7170
7171 // Convert to JS array and return.
7172 return *Factory::NewJSArrayWithElements(details);
7173}
7174
7175
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007176static Object* Runtime_GetBreakLocations(Arguments args) {
7177 HandleScope scope;
7178 ASSERT(args.length() == 1);
7179
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007180 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7181 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007182 // Find the number of break points
7183 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
7184 if (break_locations->IsUndefined()) return Heap::undefined_value();
7185 // Return array as JS array
7186 return *Factory::NewJSArrayWithElements(
7187 Handle<FixedArray>::cast(break_locations));
7188}
7189
7190
7191// Set a break point in a function
7192// args[0]: function
7193// args[1]: number: break source position (within the function source)
7194// args[2]: number: break point object
7195static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
7196 HandleScope scope;
7197 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007198 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
7199 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007200 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7201 RUNTIME_ASSERT(source_position >= 0);
7202 Handle<Object> break_point_object_arg = args.at<Object>(2);
7203
7204 // Set break point.
7205 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
7206
7207 return Heap::undefined_value();
7208}
7209
7210
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007211Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
7212 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007213 // Iterate the heap looking for SharedFunctionInfo generated from the
7214 // script. The inner most SharedFunctionInfo containing the source position
7215 // for the requested break point is found.
7216 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
7217 // which is found is not compiled it is compiled and the heap is iterated
7218 // again as the compilation might create inner functions from the newly
7219 // compiled function and the actual requested break point might be in one of
7220 // these functions.
7221 bool done = false;
7222 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00007223 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007224 Handle<SharedFunctionInfo> target;
7225 // The current candidate for the last function in script:
7226 Handle<SharedFunctionInfo> last;
7227 while (!done) {
7228 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007229 for (HeapObject* obj = iterator.next();
7230 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007231 if (obj->IsSharedFunctionInfo()) {
7232 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
7233 if (shared->script() == *script) {
7234 // If the SharedFunctionInfo found has the requested script data and
7235 // contains the source position it is a candidate.
7236 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00007237 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007238 start_position = shared->start_position();
7239 }
7240 if (start_position <= position &&
7241 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007242 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007243 // candidate this is the new candidate.
7244 if (target.is_null()) {
7245 target_start_position = start_position;
7246 target = shared;
7247 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007248 if (target_start_position == start_position &&
7249 shared->end_position() == target->end_position()) {
7250 // If a top-level function contain only one function
7251 // declartion the source for the top-level and the function is
7252 // the same. In that case prefer the non top-level function.
7253 if (!shared->is_toplevel()) {
7254 target_start_position = start_position;
7255 target = shared;
7256 }
7257 } else if (target_start_position <= start_position &&
7258 shared->end_position() <= target->end_position()) {
7259 // This containment check includes equality as a function inside
7260 // a top-level function can share either start or end position
7261 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007262 target_start_position = start_position;
7263 target = shared;
7264 }
7265 }
7266 }
7267
7268 // Keep track of the last function in the script.
7269 if (last.is_null() ||
7270 shared->end_position() > last->start_position()) {
7271 last = shared;
7272 }
7273 }
7274 }
7275 }
7276
7277 // Make sure some candidate is selected.
7278 if (target.is_null()) {
7279 if (!last.is_null()) {
7280 // Position after the last function - use last.
7281 target = last;
7282 } else {
7283 // Unable to find function - possibly script without any function.
7284 return Heap::undefined_value();
7285 }
7286 }
7287
7288 // If the candidate found is compiled we are done. NOTE: when lazy
7289 // compilation of inner functions is introduced some additional checking
7290 // needs to be done here to compile inner functions.
7291 done = target->is_compiled();
7292 if (!done) {
7293 // If the candidate is not compiled compile it to reveal any inner
7294 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007295 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007296 }
7297 }
7298
7299 return *target;
7300}
7301
7302
7303// Change the state of a break point in a script. NOTE: Regarding performance
7304// see the NOTE for GetScriptFromScriptData.
7305// args[0]: script to set break point in
7306// args[1]: number: break source position (within the script source)
7307// args[2]: number: break point object
7308static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7309 HandleScope scope;
7310 ASSERT(args.length() == 3);
7311 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7312 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7313 RUNTIME_ASSERT(source_position >= 0);
7314 Handle<Object> break_point_object_arg = args.at<Object>(2);
7315
7316 // Get the script from the script wrapper.
7317 RUNTIME_ASSERT(wrapper->value()->IsScript());
7318 Handle<Script> script(Script::cast(wrapper->value()));
7319
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007320 Object* result = Runtime::FindSharedFunctionInfoInScript(
7321 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007322 if (!result->IsUndefined()) {
7323 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7324 // Find position within function. The script position might be before the
7325 // source position of the first function.
7326 int position;
7327 if (shared->start_position() > source_position) {
7328 position = 0;
7329 } else {
7330 position = source_position - shared->start_position();
7331 }
7332 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7333 }
7334 return Heap::undefined_value();
7335}
7336
7337
7338// Clear a break point
7339// args[0]: number: break point object
7340static Object* Runtime_ClearBreakPoint(Arguments args) {
7341 HandleScope scope;
7342 ASSERT(args.length() == 1);
7343 Handle<Object> break_point_object_arg = args.at<Object>(0);
7344
7345 // Clear break point.
7346 Debug::ClearBreakPoint(break_point_object_arg);
7347
7348 return Heap::undefined_value();
7349}
7350
7351
7352// Change the state of break on exceptions
7353// args[0]: boolean indicating uncaught exceptions
7354// args[1]: boolean indicating on/off
7355static Object* Runtime_ChangeBreakOnException(Arguments args) {
7356 HandleScope scope;
7357 ASSERT(args.length() == 2);
7358 ASSERT(args[0]->IsNumber());
7359 ASSERT(args[1]->IsBoolean());
7360
7361 // Update break point state
7362 ExceptionBreakType type =
7363 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7364 bool enable = args[1]->ToBoolean()->IsTrue();
7365 Debug::ChangeBreakOnException(type, enable);
7366 return Heap::undefined_value();
7367}
7368
7369
7370// Prepare for stepping
7371// args[0]: break id for checking execution state
7372// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007373// args[2]: number of times to perform the step, for step out it is the number
7374// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007375static Object* Runtime_PrepareStep(Arguments args) {
7376 HandleScope scope;
7377 ASSERT(args.length() == 3);
7378 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007379 Object* check = Runtime_CheckExecutionState(args);
7380 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007381 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7382 return Top::Throw(Heap::illegal_argument_symbol());
7383 }
7384
7385 // Get the step action and check validity.
7386 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7387 if (step_action != StepIn &&
7388 step_action != StepNext &&
7389 step_action != StepOut &&
7390 step_action != StepInMin &&
7391 step_action != StepMin) {
7392 return Top::Throw(Heap::illegal_argument_symbol());
7393 }
7394
7395 // Get the number of steps.
7396 int step_count = NumberToInt32(args[2]);
7397 if (step_count < 1) {
7398 return Top::Throw(Heap::illegal_argument_symbol());
7399 }
7400
ager@chromium.orga1645e22009-09-09 19:27:10 +00007401 // Clear all current stepping setup.
7402 Debug::ClearStepping();
7403
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007404 // Prepare step.
7405 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7406 return Heap::undefined_value();
7407}
7408
7409
7410// Clear all stepping set by PrepareStep.
7411static Object* Runtime_ClearStepping(Arguments args) {
7412 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007413 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007414 Debug::ClearStepping();
7415 return Heap::undefined_value();
7416}
7417
7418
7419// Creates a copy of the with context chain. The copy of the context chain is
7420// is linked to the function context supplied.
7421static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7422 Handle<Context> function_context) {
7423 // At the bottom of the chain. Return the function context to link to.
7424 if (context_chain->is_function_context()) {
7425 return function_context;
7426 }
7427
7428 // Recursively copy the with contexts.
7429 Handle<Context> previous(context_chain->previous());
7430 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7431 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007432 CopyWithContextChain(function_context, previous),
7433 extension,
7434 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007435}
7436
7437
7438// Helper function to find or create the arguments object for
7439// Runtime_DebugEvaluate.
7440static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7441 Handle<JSFunction> function,
7442 Handle<Code> code,
7443 const ScopeInfo<>* sinfo,
7444 Handle<Context> function_context) {
7445 // Try to find the value of 'arguments' to pass as parameter. If it is not
7446 // found (that is the debugged function does not reference 'arguments' and
7447 // does not support eval) then create an 'arguments' object.
7448 int index;
7449 if (sinfo->number_of_stack_slots() > 0) {
7450 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7451 if (index != -1) {
7452 return Handle<Object>(frame->GetExpression(index));
7453 }
7454 }
7455
7456 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7457 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7458 NULL);
7459 if (index != -1) {
7460 return Handle<Object>(function_context->get(index));
7461 }
7462 }
7463
7464 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007465 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7466 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007467
7468 AssertNoAllocation no_gc;
7469 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007470 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007471 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007472 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007473 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007474 return arguments;
7475}
7476
7477
7478// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007479// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007480// extension part has all the parameters and locals of the function on the
7481// stack frame. A function which calls eval with the code to evaluate is then
7482// compiled in this context and called in this context. As this context
7483// replaces the context of the function on the stack frame a new (empty)
7484// function is created as well to be used as the closure for the context.
7485// This function and the context acts as replacements for the function on the
7486// stack frame presenting the same view of the values of parameters and
7487// local variables as if the piece of JavaScript was evaluated at the point
7488// where the function on the stack frame is currently stopped.
7489static Object* Runtime_DebugEvaluate(Arguments args) {
7490 HandleScope scope;
7491
7492 // Check the execution state and decode arguments frame and source to be
7493 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007494 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007495 Object* check_result = Runtime_CheckExecutionState(args);
7496 if (check_result->IsFailure()) return check_result;
7497 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7498 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007499 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7500
7501 // Handle the processing of break.
7502 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007503
7504 // Get the frame where the debugging is performed.
7505 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7506 JavaScriptFrameIterator it(id);
7507 JavaScriptFrame* frame = it.frame();
7508 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7509 Handle<Code> code(function->code());
7510 ScopeInfo<> sinfo(*code);
7511
7512 // Traverse the saved contexts chain to find the active context for the
7513 // selected frame.
7514 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007515 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007516 save = save->prev();
7517 }
7518 ASSERT(save != NULL);
7519 SaveContext savex;
7520 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007521
7522 // Create the (empty) function replacing the function on the stack frame for
7523 // the purpose of evaluating in the context created below. It is important
7524 // that this function does not describe any parameters and local variables
7525 // in the context. If it does then this will cause problems with the lookup
7526 // in Context::Lookup, where context slots for parameters and local variables
7527 // are looked at before the extension object.
7528 Handle<JSFunction> go_between =
7529 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7530 go_between->set_context(function->context());
7531#ifdef DEBUG
7532 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7533 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7534 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7535#endif
7536
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007537 // Materialize the content of the local scope into a JSObject.
7538 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007539
7540 // Allocate a new context for the debug evaluation and set the extension
7541 // object build.
7542 Handle<Context> context =
7543 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007544 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007545 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007546 Handle<Context> frame_context(Context::cast(frame->context()));
7547 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007548 context = CopyWithContextChain(frame_context, context);
7549
7550 // Wrap the evaluation statement in a new function compiled in the newly
7551 // created context. The function has one parameter which has to be called
7552 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007553 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007554 // function(arguments,__source__) {return eval(__source__);}
7555 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007556 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007557 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007558 Handle<String> function_source =
7559 Factory::NewStringFromAscii(Vector<const char>(source_str,
7560 source_str_length));
7561 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007562 Compiler::CompileEval(function_source,
7563 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007564 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007565 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007566 if (boilerplate.is_null()) return Failure::Exception();
7567 Handle<JSFunction> compiled_function =
7568 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7569
7570 // Invoke the result of the compilation to get the evaluation function.
7571 bool has_pending_exception;
7572 Handle<Object> receiver(frame->receiver());
7573 Handle<Object> evaluation_function =
7574 Execution::Call(compiled_function, receiver, 0, NULL,
7575 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007576 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007577
7578 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7579 function_context);
7580
7581 // Invoke the evaluation function and return the result.
7582 const int argc = 2;
7583 Object** argv[argc] = { arguments.location(),
7584 Handle<Object>::cast(source).location() };
7585 Handle<Object> result =
7586 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7587 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007588 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007589
7590 // Skip the global proxy as it has no properties and always delegates to the
7591 // real global object.
7592 if (result->IsJSGlobalProxy()) {
7593 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7594 }
7595
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007596 return *result;
7597}
7598
7599
7600static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7601 HandleScope scope;
7602
7603 // Check the execution state and decode arguments frame and source to be
7604 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007605 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007606 Object* check_result = Runtime_CheckExecutionState(args);
7607 if (check_result->IsFailure()) return check_result;
7608 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007609 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7610
7611 // Handle the processing of break.
7612 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007613
7614 // Enter the top context from before the debugger was invoked.
7615 SaveContext save;
7616 SaveContext* top = &save;
7617 while (top != NULL && *top->context() == *Debug::debug_context()) {
7618 top = top->prev();
7619 }
7620 if (top != NULL) {
7621 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007622 }
7623
7624 // Get the global context now set to the top context from before the
7625 // debugger was invoked.
7626 Handle<Context> context = Top::global_context();
7627
7628 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007629 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007630 Handle<JSFunction>(Compiler::CompileEval(source,
7631 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007632 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007633 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007634 if (boilerplate.is_null()) return Failure::Exception();
7635 Handle<JSFunction> compiled_function =
7636 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7637 context));
7638
7639 // Invoke the result of the compilation to get the evaluation function.
7640 bool has_pending_exception;
7641 Handle<Object> receiver = Top::global();
7642 Handle<Object> result =
7643 Execution::Call(compiled_function, receiver, 0, NULL,
7644 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007645 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007646 return *result;
7647}
7648
7649
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007650static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7651 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007652 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007653
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007654 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007655 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007656
7657 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007658 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007659 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7660 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7661 // because using
7662 // instances->set(i, *GetScriptWrapper(script))
7663 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7664 // already have deferenced the instances handle.
7665 Handle<JSValue> wrapper = GetScriptWrapper(script);
7666 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007667 }
7668
7669 // Return result as a JS array.
7670 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7671 Handle<JSArray>::cast(result)->SetContent(*instances);
7672 return *result;
7673}
7674
7675
7676// Helper function used by Runtime_DebugReferencedBy below.
7677static int DebugReferencedBy(JSObject* target,
7678 Object* instance_filter, int max_references,
7679 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007680 JSFunction* arguments_function) {
7681 NoHandleAllocation ha;
7682 AssertNoAllocation no_alloc;
7683
7684 // Iterate the heap.
7685 int count = 0;
7686 JSObject* last = NULL;
7687 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007688 HeapObject* heap_obj = NULL;
7689 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007690 (max_references == 0 || count < max_references)) {
7691 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007692 if (heap_obj->IsJSObject()) {
7693 // Skip context extension objects and argument arrays as these are
7694 // checked in the context of functions using them.
7695 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007696 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007697 obj->map()->constructor() == arguments_function) {
7698 continue;
7699 }
7700
7701 // Check if the JS object has a reference to the object looked for.
7702 if (obj->ReferencesObject(target)) {
7703 // Check instance filter if supplied. This is normally used to avoid
7704 // references from mirror objects (see Runtime_IsInPrototypeChain).
7705 if (!instance_filter->IsUndefined()) {
7706 Object* V = obj;
7707 while (true) {
7708 Object* prototype = V->GetPrototype();
7709 if (prototype->IsNull()) {
7710 break;
7711 }
7712 if (instance_filter == prototype) {
7713 obj = NULL; // Don't add this object.
7714 break;
7715 }
7716 V = prototype;
7717 }
7718 }
7719
7720 if (obj != NULL) {
7721 // Valid reference found add to instance array if supplied an update
7722 // count.
7723 if (instances != NULL && count < instances_size) {
7724 instances->set(count, obj);
7725 }
7726 last = obj;
7727 count++;
7728 }
7729 }
7730 }
7731 }
7732
7733 // Check for circular reference only. This can happen when the object is only
7734 // referenced from mirrors and has a circular reference in which case the
7735 // object is not really alive and would have been garbage collected if not
7736 // referenced from the mirror.
7737 if (count == 1 && last == target) {
7738 count = 0;
7739 }
7740
7741 // Return the number of referencing objects found.
7742 return count;
7743}
7744
7745
7746// Scan the heap for objects with direct references to an object
7747// args[0]: the object to find references to
7748// args[1]: constructor function for instances to exclude (Mirror)
7749// args[2]: the the maximum number of objects to return
7750static Object* Runtime_DebugReferencedBy(Arguments args) {
7751 ASSERT(args.length() == 3);
7752
7753 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007754 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007755
7756 // Check parameters.
7757 CONVERT_CHECKED(JSObject, target, args[0]);
7758 Object* instance_filter = args[1];
7759 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7760 instance_filter->IsJSObject());
7761 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7762 RUNTIME_ASSERT(max_references >= 0);
7763
7764 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007765 JSObject* arguments_boilerplate =
7766 Top::context()->global_context()->arguments_boilerplate();
7767 JSFunction* arguments_function =
7768 JSFunction::cast(arguments_boilerplate->map()->constructor());
7769
7770 // Get the number of referencing objects.
7771 int count;
7772 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007773 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007774
7775 // Allocate an array to hold the result.
7776 Object* object = Heap::AllocateFixedArray(count);
7777 if (object->IsFailure()) return object;
7778 FixedArray* instances = FixedArray::cast(object);
7779
7780 // Fill the referencing objects.
7781 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007782 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007783
7784 // Return result as JS array.
7785 Object* result =
7786 Heap::AllocateJSObject(
7787 Top::context()->global_context()->array_function());
7788 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7789 return result;
7790}
7791
7792
7793// Helper function used by Runtime_DebugConstructedBy below.
7794static int DebugConstructedBy(JSFunction* constructor, int max_references,
7795 FixedArray* instances, int instances_size) {
7796 AssertNoAllocation no_alloc;
7797
7798 // Iterate the heap.
7799 int count = 0;
7800 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007801 HeapObject* heap_obj = NULL;
7802 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007803 (max_references == 0 || count < max_references)) {
7804 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007805 if (heap_obj->IsJSObject()) {
7806 JSObject* obj = JSObject::cast(heap_obj);
7807 if (obj->map()->constructor() == constructor) {
7808 // Valid reference found add to instance array if supplied an update
7809 // count.
7810 if (instances != NULL && count < instances_size) {
7811 instances->set(count, obj);
7812 }
7813 count++;
7814 }
7815 }
7816 }
7817
7818 // Return the number of referencing objects found.
7819 return count;
7820}
7821
7822
7823// Scan the heap for objects constructed by a specific function.
7824// args[0]: the constructor to find instances of
7825// args[1]: the the maximum number of objects to return
7826static Object* Runtime_DebugConstructedBy(Arguments args) {
7827 ASSERT(args.length() == 2);
7828
7829 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007830 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007831
7832 // Check parameters.
7833 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7834 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7835 RUNTIME_ASSERT(max_references >= 0);
7836
7837 // Get the number of referencing objects.
7838 int count;
7839 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7840
7841 // Allocate an array to hold the result.
7842 Object* object = Heap::AllocateFixedArray(count);
7843 if (object->IsFailure()) return object;
7844 FixedArray* instances = FixedArray::cast(object);
7845
7846 // Fill the referencing objects.
7847 count = DebugConstructedBy(constructor, max_references, instances, count);
7848
7849 // Return result as JS array.
7850 Object* result =
7851 Heap::AllocateJSObject(
7852 Top::context()->global_context()->array_function());
7853 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7854 return result;
7855}
7856
7857
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007858// Find the effective prototype object as returned by __proto__.
7859// args[0]: the object to find the prototype for.
7860static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007861 ASSERT(args.length() == 1);
7862
7863 CONVERT_CHECKED(JSObject, obj, args[0]);
7864
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007865 // Use the __proto__ accessor.
7866 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007867}
7868
7869
7870static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007871 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007872 CPU::DebugBreak();
7873 return Heap::undefined_value();
7874}
7875
7876
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007877static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007878#ifdef DEBUG
7879 HandleScope scope;
7880 ASSERT(args.length() == 1);
7881 // Get the function and make sure it is compiled.
7882 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007883 Handle<SharedFunctionInfo> shared(func->shared());
7884 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007885 return Failure::Exception();
7886 }
7887 func->code()->PrintLn();
7888#endif // DEBUG
7889 return Heap::undefined_value();
7890}
ager@chromium.org9085a012009-05-11 19:22:57 +00007891
7892
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007893static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7894#ifdef DEBUG
7895 HandleScope scope;
7896 ASSERT(args.length() == 1);
7897 // Get the function and make sure it is compiled.
7898 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007899 Handle<SharedFunctionInfo> shared(func->shared());
7900 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007901 return Failure::Exception();
7902 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007903 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007904#endif // DEBUG
7905 return Heap::undefined_value();
7906}
7907
7908
ager@chromium.org9085a012009-05-11 19:22:57 +00007909static Object* Runtime_FunctionGetInferredName(Arguments args) {
7910 NoHandleAllocation ha;
7911 ASSERT(args.length() == 1);
7912
7913 CONVERT_CHECKED(JSFunction, f, args[0]);
7914 return f->shared()->inferred_name();
7915}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007916
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007917#endif // ENABLE_DEBUGGER_SUPPORT
7918
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007919#ifdef ENABLE_LOGGING_AND_PROFILING
7920
7921static Object* Runtime_ProfilerResume(Arguments args) {
7922 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00007923 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007924
7925 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00007926 CONVERT_CHECKED(Smi, smi_tag, args[1]);
7927 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007928 return Heap::undefined_value();
7929}
7930
7931
7932static Object* Runtime_ProfilerPause(Arguments args) {
7933 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00007934 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007935
7936 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00007937 CONVERT_CHECKED(Smi, smi_tag, args[1]);
7938 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007939 return Heap::undefined_value();
7940}
7941
7942#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007943
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007944// Finds the script object from the script data. NOTE: This operation uses
7945// heap traversal to find the function generated for the source position
7946// for the requested break point. For lazily compiled functions several heap
7947// traversals might be required rendering this operation as a rather slow
7948// operation. However for setting break points which is normally done through
7949// some kind of user interaction the performance is not crucial.
7950static Handle<Object> Runtime_GetScriptFromScriptName(
7951 Handle<String> script_name) {
7952 // Scan the heap for Script objects to find the script with the requested
7953 // script data.
7954 Handle<Script> script;
7955 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007956 HeapObject* obj = NULL;
7957 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007958 // If a script is found check if it has the script data requested.
7959 if (obj->IsScript()) {
7960 if (Script::cast(obj)->name()->IsString()) {
7961 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7962 script = Handle<Script>(Script::cast(obj));
7963 }
7964 }
7965 }
7966 }
7967
7968 // If no script with the requested script data is found return undefined.
7969 if (script.is_null()) return Factory::undefined_value();
7970
7971 // Return the script found.
7972 return GetScriptWrapper(script);
7973}
7974
7975
7976// Get the script object from script data. NOTE: Regarding performance
7977// see the NOTE for GetScriptFromScriptData.
7978// args[0]: script data for the script to find the source for
7979static Object* Runtime_GetScript(Arguments args) {
7980 HandleScope scope;
7981
7982 ASSERT(args.length() == 1);
7983
7984 CONVERT_CHECKED(String, script_name, args[0]);
7985
7986 // Find the requested script.
7987 Handle<Object> result =
7988 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7989 return *result;
7990}
7991
7992
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007993// Determines whether the given stack frame should be displayed in
7994// a stack trace. The caller is the error constructor that asked
7995// for the stack trace to be collected. The first time a construct
7996// call to this function is encountered it is skipped. The seen_caller
7997// in/out parameter is used to remember if the caller has been seen
7998// yet.
7999static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
8000 bool* seen_caller) {
8001 // Only display JS frames.
8002 if (!raw_frame->is_java_script())
8003 return false;
8004 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
8005 Object* raw_fun = frame->function();
8006 // Not sure when this can happen but skip it just in case.
8007 if (!raw_fun->IsJSFunction())
8008 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008009 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008010 *seen_caller = true;
8011 return false;
8012 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008013 // Skip all frames until we've seen the caller. Also, skip the most
8014 // obvious builtin calls. Some builtin calls (such as Number.ADD
8015 // which is invoked using 'call') are very difficult to recognize
8016 // so we're leaving them in for now.
8017 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008018}
8019
8020
8021// Collect the raw data for a stack trace. Returns an array of three
8022// element segments each containing a receiver, function and native
8023// code offset.
8024static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008025 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008026 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008027 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
8028
8029 HandleScope scope;
8030
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00008031 limit = Max(limit, 0); // Ensure that limit is not negative.
8032 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008033 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008034
8035 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008036 // If the caller parameter is a function we skip frames until we're
8037 // under it before starting to collect.
8038 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008039 int cursor = 0;
8040 int frames_seen = 0;
8041 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008042 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008043 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008044 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008045 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008046 Object* recv = frame->receiver();
8047 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008048 Address pc = frame->pc();
8049 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008050 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008051 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008052 if (cursor + 2 < elements->length()) {
8053 elements->set(cursor++, recv);
8054 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008055 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008056 } else {
8057 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00008058 Handle<Object> recv_handle(recv);
8059 Handle<Object> fun_handle(fun);
8060 SetElement(result, cursor++, recv_handle);
8061 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008062 SetElement(result, cursor++, Handle<Smi>(offset));
8063 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008064 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008065 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008066 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00008067
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008068 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008069 return *result;
8070}
8071
8072
ager@chromium.org3811b432009-10-28 14:53:37 +00008073// Returns V8 version as a string.
8074static Object* Runtime_GetV8Version(Arguments args) {
8075 ASSERT_EQ(args.length(), 0);
8076
8077 NoHandleAllocation ha;
8078
8079 const char* version_string = v8::V8::GetVersion();
8080
8081 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
8082}
8083
8084
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008085static Object* Runtime_Abort(Arguments args) {
8086 ASSERT(args.length() == 2);
8087 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
8088 Smi::cast(args[1])->value());
8089 Top::PrintStack();
8090 OS::Abort();
8091 UNREACHABLE();
8092 return NULL;
8093}
8094
8095
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008096static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
8097 ASSERT(args.length() == 0);
8098 HandleScope::DeleteExtensions();
8099 return Heap::undefined_value();
8100}
8101
8102
kasper.lund44510672008-07-25 07:37:58 +00008103#ifdef DEBUG
8104// ListNatives is ONLY used by the fuzz-natives.js in debug mode
8105// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008106static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008107 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008108 HandleScope scope;
8109 Handle<JSArray> result = Factory::NewJSArray(0);
8110 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00008111#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008112 { \
8113 HandleScope inner; \
8114 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008115 Factory::NewStringFromAscii( \
8116 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008117 Handle<JSArray> pair = Factory::NewJSArray(0); \
8118 SetElement(pair, 0, name); \
8119 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
8120 SetElement(result, index++, pair); \
8121 }
8122 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
8123#undef ADD_ENTRY
8124 return *result;
8125}
kasper.lund44510672008-07-25 07:37:58 +00008126#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008127
8128
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008129static Object* Runtime_Log(Arguments args) {
8130 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00008131 CONVERT_CHECKED(String, format, args[0]);
8132 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008133 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008134 Logger::LogRuntime(chars, elms);
8135 return Heap::undefined_value();
8136}
8137
8138
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008139static Object* Runtime_IS_VAR(Arguments args) {
8140 UNREACHABLE(); // implemented as macro in the parser
8141 return NULL;
8142}
8143
8144
8145// ----------------------------------------------------------------------------
8146// Implementation of Runtime
8147
ager@chromium.orga1645e22009-09-09 19:27:10 +00008148#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008149 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00008150 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008151
8152static Runtime::Function Runtime_functions[] = {
8153 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008154 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008155};
8156
8157#undef F
8158
8159
8160Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
8161 ASSERT(0 <= fid && fid < kNofFunctions);
8162 return &Runtime_functions[fid];
8163}
8164
8165
8166Runtime::Function* Runtime::FunctionForName(const char* name) {
8167 for (Function* f = Runtime_functions; f->name != NULL; f++) {
8168 if (strcmp(f->name, name) == 0) {
8169 return f;
8170 }
8171 }
8172 return NULL;
8173}
8174
8175
8176void Runtime::PerformGC(Object* result) {
8177 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008178 if (failure->IsRetryAfterGC()) {
8179 // Try to do a garbage collection; ignore it if it fails. The C
8180 // entry stub will throw an out-of-memory exception in that case.
8181 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
8182 } else {
8183 // Handle last resort GC and make sure to allow future allocations
8184 // to grow the heap without causing GCs (if possible).
8185 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008186 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008187 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008188}
8189
8190
8191} } // namespace v8::internal