blob: 40b37d96e1dcb4405cb0bf0b94ad5b1997e53686 [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"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 } else if (Array::IndexFromObject(*key, &element_index)) {
295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
572// Returns an array with the property description:
573// if args[1] is not a property on args[0]
574// returns undefined
575// if args[1] is a data property on args[0]
576// [false, value, Writeable, Enumerable, Configurable]
577// if args[1] is an accessor on args[0]
578// [true, GetFunction, SetFunction, Enumerable, Configurable]
579static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000580 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000581 HandleScope scope;
582 Handle<FixedArray> elms = Factory::NewFixedArray(5);
583 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
584 LookupResult result;
585 CONVERT_CHECKED(JSObject, obj, args[0]);
586 CONVERT_CHECKED(String, name, args[1]);
587
588 // Use recursive implementation to also traverse hidden prototypes
589 GetOwnPropertyImplementation(obj, name, &result);
590
591 if (!result.IsProperty())
592 return Heap::undefined_value();
593
594 if (result.type() == CALLBACKS) {
595 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000596 if (structure->IsProxy() || structure->IsAccessorInfo()) {
597 // Property that is internally implemented as a callback or
598 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000599 Object* value = obj->GetPropertyWithCallback(
600 obj, structure, name, result.holder());
601 elms->set(0, Heap::false_value());
602 elms->set(1, value);
603 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
604 } else if (structure->IsFixedArray()) {
605 // __defineGetter__/__defineSetter__ callback.
606 elms->set(0, Heap::true_value());
607 elms->set(1, FixedArray::cast(structure)->get(0));
608 elms->set(2, FixedArray::cast(structure)->get(1));
609 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000610 return Heap::undefined_value();
611 }
612 } else {
613 elms->set(0, Heap::false_value());
614 elms->set(1, result.GetLazyValue());
615 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
616 }
617
618 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000619 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000620 return *desc;
621}
622
623
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000624static Object* Runtime_IsExtensible(Arguments args) {
625 ASSERT(args.length() == 1);
626 CONVERT_CHECKED(JSObject, obj, args[0]);
627 return obj->map()->is_extensible() ? Heap::true_value()
628 : Heap::false_value();
629}
630
631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000632static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000633 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000635 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
636 CONVERT_ARG_CHECKED(String, pattern, 1);
637 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000638 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
639 if (result.is_null()) return Failure::Exception();
640 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641}
642
643
644static Object* Runtime_CreateApiFunction(Arguments args) {
645 HandleScope scope;
646 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000647 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000648 return *Factory::CreateApiFunction(data);
649}
650
651
652static Object* Runtime_IsTemplate(Arguments args) {
653 ASSERT(args.length() == 1);
654 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000655 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 return Heap::ToBoolean(result);
657}
658
659
660static Object* Runtime_GetTemplateField(Arguments args) {
661 ASSERT(args.length() == 2);
662 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000664 int index = field->value();
665 int offset = index * kPointerSize + HeapObject::kHeaderSize;
666 InstanceType type = templ->map()->instance_type();
667 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
668 type == OBJECT_TEMPLATE_INFO_TYPE);
669 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000670 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000671 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
672 } else {
673 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
674 }
675 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676}
677
678
ager@chromium.org870a0b62008-11-04 11:43:05 +0000679static Object* Runtime_DisableAccessChecks(Arguments args) {
680 ASSERT(args.length() == 1);
681 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000682 Map* old_map = object->map();
683 bool needs_access_checks = old_map->is_access_check_needed();
684 if (needs_access_checks) {
685 // Copy map so it won't interfere constructor's initial map.
686 Object* new_map = old_map->CopyDropTransitions();
687 if (new_map->IsFailure()) return new_map;
688
689 Map::cast(new_map)->set_is_access_check_needed(false);
690 object->set_map(Map::cast(new_map));
691 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000692 return needs_access_checks ? Heap::true_value() : Heap::false_value();
693}
694
695
696static Object* Runtime_EnableAccessChecks(Arguments args) {
697 ASSERT(args.length() == 1);
698 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000699 Map* old_map = object->map();
700 if (!old_map->is_access_check_needed()) {
701 // Copy map so it won't interfere constructor's initial map.
702 Object* new_map = old_map->CopyDropTransitions();
703 if (new_map->IsFailure()) return new_map;
704
705 Map::cast(new_map)->set_is_access_check_needed(true);
706 object->set_map(Map::cast(new_map));
707 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000708 return Heap::undefined_value();
709}
710
711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000712static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
713 HandleScope scope;
714 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
715 Handle<Object> args[2] = { type_handle, name };
716 Handle<Object> error =
717 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
718 return Top::Throw(*error);
719}
720
721
722static Object* Runtime_DeclareGlobals(Arguments args) {
723 HandleScope scope;
724 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
725
ager@chromium.org3811b432009-10-28 14:53:37 +0000726 Handle<Context> context = args.at<Context>(0);
727 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 bool is_eval = Smi::cast(args[2])->value() == 1;
729
730 // Compute the property attributes. According to ECMA-262, section
731 // 13, page 71, the property must be read-only and
732 // non-deletable. However, neither SpiderMonkey nor KJS creates the
733 // property as read-only, so we don't either.
734 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Traverse the name/value pairs and set the properties.
737 int length = pairs->length();
738 for (int i = 0; i < length; i += 2) {
739 HandleScope scope;
740 Handle<String> name(String::cast(pairs->get(i)));
741 Handle<Object> value(pairs->get(i + 1));
742
743 // We have to declare a global const property. To capture we only
744 // assign to it when evaluating the assignment for "const x =
745 // <expr>" the initial value is the hole.
746 bool is_const_property = value->IsTheHole();
747
748 if (value->IsUndefined() || is_const_property) {
749 // Lookup the property in the global object, and don't set the
750 // value of the variable if the property is already there.
751 LookupResult lookup;
752 global->Lookup(*name, &lookup);
753 if (lookup.IsProperty()) {
754 // Determine if the property is local by comparing the holder
755 // against the global object. The information will be used to
756 // avoid throwing re-declaration errors when declaring
757 // variables or constants that exist in the prototype chain.
758 bool is_local = (*global == lookup.holder());
759 // Get the property attributes and determine if the property is
760 // read-only.
761 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
762 bool is_read_only = (attributes & READ_ONLY) != 0;
763 if (lookup.type() == INTERCEPTOR) {
764 // If the interceptor says the property is there, we
765 // just return undefined without overwriting the property.
766 // Otherwise, we continue to setting the property.
767 if (attributes != ABSENT) {
768 // Check if the existing property conflicts with regards to const.
769 if (is_local && (is_read_only || is_const_property)) {
770 const char* type = (is_read_only) ? "const" : "var";
771 return ThrowRedeclarationError(type, name);
772 };
773 // The property already exists without conflicting: Go to
774 // the next declaration.
775 continue;
776 }
777 // Fall-through and introduce the absent property by using
778 // SetProperty.
779 } else {
780 if (is_local && (is_read_only || is_const_property)) {
781 const char* type = (is_read_only) ? "const" : "var";
782 return ThrowRedeclarationError(type, name);
783 }
784 // The property already exists without conflicting: Go to
785 // the next declaration.
786 continue;
787 }
788 }
789 } else {
790 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000791 Handle<SharedFunctionInfo> shared =
792 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000794 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795 value = function;
796 }
797
798 LookupResult lookup;
799 global->LocalLookup(*name, &lookup);
800
801 PropertyAttributes attributes = is_const_property
802 ? static_cast<PropertyAttributes>(base | READ_ONLY)
803 : base;
804
805 if (lookup.IsProperty()) {
806 // There's a local property that we need to overwrite because
807 // we're either declaring a function or there's an interceptor
808 // that claims the property is absent.
809
810 // Check for conflicting re-declarations. We cannot have
811 // conflicting types in case of intercepted properties because
812 // they are absent.
813 if (lookup.type() != INTERCEPTOR &&
814 (lookup.IsReadOnly() || is_const_property)) {
815 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
816 return ThrowRedeclarationError(type, name);
817 }
818 SetProperty(global, name, value, attributes);
819 } else {
820 // If a property with this name does not already exist on the
821 // global object add the property locally. We take special
822 // precautions to always add it as a local property even in case
823 // of callbacks in the prototype chain (this rules out using
824 // SetProperty). Also, we must use the handle-based version to
825 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000826 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 }
828 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 return Heap::undefined_value();
831}
832
833
834static Object* Runtime_DeclareContextSlot(Arguments args) {
835 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000836 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 CONVERT_ARG_CHECKED(Context, context, 0);
839 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000841 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000843 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000844
845 // Declarations are always done in the function context.
846 context = Handle<Context>(context->fcontext());
847
848 int index;
849 PropertyAttributes attributes;
850 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000851 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 context->Lookup(name, flags, &index, &attributes);
853
854 if (attributes != ABSENT) {
855 // The name was declared before; check for conflicting
856 // re-declarations: This is similar to the code in parser.cc in
857 // the AstBuildingParser::Declare function.
858 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
859 // Functions are not read-only.
860 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
861 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
862 return ThrowRedeclarationError(type, name);
863 }
864
865 // Initialize it if necessary.
866 if (*initial_value != NULL) {
867 if (index >= 0) {
868 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000869 // the function context or the arguments object.
870 if (holder->IsContext()) {
871 ASSERT(holder.is_identical_to(context));
872 if (((attributes & READ_ONLY) == 0) ||
873 context->get(index)->IsTheHole()) {
874 context->set(index, *initial_value);
875 }
876 } else {
877 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 }
879 } else {
880 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000881 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 SetProperty(context_ext, name, initial_value, mode);
883 }
884 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000886 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000887 // The property is not in the function context. It needs to be
888 // "declared" in the function context's extension context, or in the
889 // global context.
890 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000891 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892 // The function context's extension context exists - use it.
893 context_ext = Handle<JSObject>(context->extension());
894 } else {
895 // The function context's extension context does not exists - allocate
896 // it.
897 context_ext = Factory::NewJSObject(Top::context_extension_function());
898 // And store it in the extension slot.
899 context->set_extension(*context_ext);
900 }
901 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902
ager@chromium.org7c537e22008-10-16 08:43:32 +0000903 // Declare the property by setting it to the initial value if provided,
904 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
905 // constant declarations).
906 ASSERT(!context_ext->HasLocalProperty(*name));
907 Handle<Object> value(Heap::undefined_value());
908 if (*initial_value != NULL) value = initial_value;
909 SetProperty(context_ext, name, value, mode);
910 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
911 }
912
913 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914}
915
916
917static Object* Runtime_InitializeVarGlobal(Arguments args) {
918 NoHandleAllocation nha;
919
920 // Determine if we need to assign to the variable if it already
921 // exists (based on the number of arguments).
922 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
923 bool assign = args.length() == 2;
924
925 CONVERT_ARG_CHECKED(String, name, 0);
926 GlobalObject* global = Top::context()->global();
927
928 // According to ECMA-262, section 12.2, page 62, the property must
929 // not be deletable.
930 PropertyAttributes attributes = DONT_DELETE;
931
932 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000933 // there, there is a property with this name in the prototype chain.
934 // We follow Safari and Firefox behavior and only set the property
935 // locally if there is an explicit initialization value that we have
936 // to assign to the property. When adding the property we take
937 // special precautions to always add it as a local property even in
938 // case of callbacks in the prototype chain (this rules out using
939 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
940 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000941 // Note that objects can have hidden prototypes, so we need to traverse
942 // the whole chain of hidden prototypes to do a 'local' lookup.
943 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000945 while (true) {
946 real_holder->LocalLookup(*name, &lookup);
947 if (lookup.IsProperty()) {
948 // Determine if this is a redeclaration of something read-only.
949 if (lookup.IsReadOnly()) {
950 // If we found readonly property on one of hidden prototypes,
951 // just shadow it.
952 if (real_holder != Top::context()->global()) break;
953 return ThrowRedeclarationError("const", name);
954 }
955
956 // Determine if this is a redeclaration of an intercepted read-only
957 // property and figure out if the property exists at all.
958 bool found = true;
959 PropertyType type = lookup.type();
960 if (type == INTERCEPTOR) {
961 HandleScope handle_scope;
962 Handle<JSObject> holder(real_holder);
963 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
964 real_holder = *holder;
965 if (intercepted == ABSENT) {
966 // The interceptor claims the property isn't there. We need to
967 // make sure to introduce it.
968 found = false;
969 } else if ((intercepted & READ_ONLY) != 0) {
970 // The property is present, but read-only. Since we're trying to
971 // overwrite it with a variable declaration we must throw a
972 // re-declaration error. However if we found readonly property
973 // on one of hidden prototypes, just shadow it.
974 if (real_holder != Top::context()->global()) break;
975 return ThrowRedeclarationError("const", name);
976 }
977 }
978
979 if (found && !assign) {
980 // The global property is there and we're not assigning any value
981 // to it. Just return.
982 return Heap::undefined_value();
983 }
984
985 // Assign the value (or undefined) to the property.
986 Object* value = (assign) ? args[1] : Heap::undefined_value();
987 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000988 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000989
990 Object* proto = real_holder->GetPrototype();
991 if (!proto->IsJSObject())
992 break;
993
994 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
995 break;
996
997 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998 }
999
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001000 global = Top::context()->global();
1001 if (assign) {
1002 return global->IgnoreAttributesAndSetLocalProperty(*name,
1003 args[1],
1004 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007}
1008
1009
1010static Object* Runtime_InitializeConstGlobal(Arguments args) {
1011 // All constants are declared with an initial value. The name
1012 // of the constant is the first argument and the initial value
1013 // is the second.
1014 RUNTIME_ASSERT(args.length() == 2);
1015 CONVERT_ARG_CHECKED(String, name, 0);
1016 Handle<Object> value = args.at<Object>(1);
1017
1018 // Get the current global object from top.
1019 GlobalObject* global = Top::context()->global();
1020
1021 // According to ECMA-262, section 12.2, page 62, the property must
1022 // not be deletable. Since it's a const, it must be READ_ONLY too.
1023 PropertyAttributes attributes =
1024 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1025
1026 // Lookup the property locally in the global object. If it isn't
1027 // there, we add the property and take special precautions to always
1028 // add it as a local property even in case of callbacks in the
1029 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031 LookupResult lookup;
1032 global->LocalLookup(*name, &lookup);
1033 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001034 return global->IgnoreAttributesAndSetLocalProperty(*name,
1035 *value,
1036 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 }
1038
1039 // Determine if this is a redeclaration of something not
1040 // read-only. In case the result is hidden behind an interceptor we
1041 // need to ask it for the property attributes.
1042 if (!lookup.IsReadOnly()) {
1043 if (lookup.type() != INTERCEPTOR) {
1044 return ThrowRedeclarationError("var", name);
1045 }
1046
1047 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1048
1049 // Throw re-declaration error if the intercepted property is present
1050 // but not read-only.
1051 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1052 return ThrowRedeclarationError("var", name);
1053 }
1054
1055 // Restore global object from context (in case of GC) and continue
1056 // with setting the value because the property is either absent or
1057 // read-only. We also have to do redo the lookup.
1058 global = Top::context()->global();
1059
1060 // BUG 1213579: Handle the case where we have to set a read-only
1061 // property through an interceptor and only do it if it's
1062 // uninitialized, e.g. the hole. Nirk...
1063 global->SetProperty(*name, *value, attributes);
1064 return *value;
1065 }
1066
1067 // Set the value, but only we're assigning the initial value to a
1068 // constant. For now, we determine this by checking if the
1069 // current value is the hole.
1070 PropertyType type = lookup.type();
1071 if (type == FIELD) {
1072 FixedArray* properties = global->properties();
1073 int index = lookup.GetFieldIndex();
1074 if (properties->get(index)->IsTheHole()) {
1075 properties->set(index, *value);
1076 }
1077 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001078 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1079 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
1081 } else {
1082 // Ignore re-initialization of constants that have already been
1083 // assigned a function value.
1084 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1085 }
1086
1087 // Use the set value as the result of the operation.
1088 return *value;
1089}
1090
1091
1092static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1093 HandleScope scope;
1094 ASSERT(args.length() == 3);
1095
1096 Handle<Object> value(args[0]);
1097 ASSERT(!value->IsTheHole());
1098 CONVERT_ARG_CHECKED(Context, context, 1);
1099 Handle<String> name(String::cast(args[2]));
1100
1101 // Initializations are always done in the function context.
1102 context = Handle<Context>(context->fcontext());
1103
1104 int index;
1105 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001106 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001107 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001108 context->Lookup(name, flags, &index, &attributes);
1109
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001110 // In most situations, the property introduced by the const
1111 // declaration should be present in the context extension object.
1112 // However, because declaration and initialization are separate, the
1113 // property might have been deleted (if it was introduced by eval)
1114 // before we reach the initialization point.
1115 //
1116 // Example:
1117 //
1118 // function f() { eval("delete x; const x;"); }
1119 //
1120 // In that case, the initialization behaves like a normal assignment
1121 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001123 // Property was found in a context.
1124 if (holder->IsContext()) {
1125 // The holder cannot be the function context. If it is, there
1126 // should have been a const redeclaration error when declaring
1127 // the const property.
1128 ASSERT(!holder.is_identical_to(context));
1129 if ((attributes & READ_ONLY) == 0) {
1130 Handle<Context>::cast(holder)->set(index, *value);
1131 }
1132 } else {
1133 // The holder is an arguments object.
1134 ASSERT((attributes & READ_ONLY) == 0);
1135 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137 return *value;
1138 }
1139
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001140 // The property could not be found, we introduce it in the global
1141 // context.
1142 if (attributes == ABSENT) {
1143 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1144 SetProperty(global, name, value, NONE);
1145 return *value;
1146 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001147
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001148 // The property was present in a context extension object.
1149 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001151 if (*context_ext == context->extension()) {
1152 // This is the property that was introduced by the const
1153 // declaration. Set it if it hasn't been set before. NOTE: We
1154 // cannot use GetProperty() to get the current value as it
1155 // 'unholes' the value.
1156 LookupResult lookup;
1157 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1158 ASSERT(lookup.IsProperty()); // the property was declared
1159 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1160
1161 PropertyType type = lookup.type();
1162 if (type == FIELD) {
1163 FixedArray* properties = context_ext->properties();
1164 int index = lookup.GetFieldIndex();
1165 if (properties->get(index)->IsTheHole()) {
1166 properties->set(index, *value);
1167 }
1168 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001169 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1170 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001171 }
1172 } else {
1173 // We should not reach here. Any real, named property should be
1174 // either a field or a dictionary slot.
1175 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176 }
1177 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001178 // The property was found in a different context extension object.
1179 // Set it if it is not a read-only property.
1180 if ((attributes & READ_ONLY) == 0) {
1181 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1182 // Setting a property might throw an exception. Exceptions
1183 // are converted to empty handles in handle operations. We
1184 // need to convert back to exceptions here.
1185 if (set.is_null()) {
1186 ASSERT(Top::has_pending_exception());
1187 return Failure::Exception();
1188 }
1189 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 return *value;
1193}
1194
1195
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001196static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1197 Arguments args) {
1198 HandleScope scope;
1199 ASSERT(args.length() == 2);
1200 CONVERT_ARG_CHECKED(JSObject, object, 0);
1201 CONVERT_SMI_CHECKED(properties, args[1]);
1202 if (object->HasFastProperties()) {
1203 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1204 }
1205 return *object;
1206}
1207
1208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209static Object* Runtime_RegExpExec(Arguments args) {
1210 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001211 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001212 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1213 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001214 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001215 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001216 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001217 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001218 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001219 RUNTIME_ASSERT(index >= 0);
1220 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001221 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001222 Handle<Object> result = RegExpImpl::Exec(regexp,
1223 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001224 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001225 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001226 if (result.is_null()) return Failure::Exception();
1227 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228}
1229
1230
lrn@chromium.org25156de2010-04-06 13:10:27 +00001231static Object* Runtime_RegExpInitializeObject(Arguments args) {
1232 AssertNoAllocation no_alloc;
1233 ASSERT(args.length() == 5);
1234 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1235 CONVERT_CHECKED(String, source, args[1]);
1236
1237 Object* global = args[2];
1238 if (!global->IsTrue()) global = Heap::false_value();
1239
1240 Object* ignoreCase = args[3];
1241 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1242
1243 Object* multiline = args[4];
1244 if (!multiline->IsTrue()) multiline = Heap::false_value();
1245
1246 Map* map = regexp->map();
1247 Object* constructor = map->constructor();
1248 if (constructor->IsJSFunction() &&
1249 JSFunction::cast(constructor)->initial_map() == map) {
1250 // If we still have the original map, set in-object properties directly.
1251 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1252 // TODO(lrn): Consider skipping write barrier on booleans as well.
1253 // Both true and false should be in oldspace at all times.
1254 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1255 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1256 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1257 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1258 Smi::FromInt(0),
1259 SKIP_WRITE_BARRIER);
1260 return regexp;
1261 }
1262
1263 // Map has changed, so use generic, but slower, method.
1264 PropertyAttributes final =
1265 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1266 PropertyAttributes writable =
1267 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1268 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1269 source,
1270 final);
1271 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1272 global,
1273 final);
1274 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1275 ignoreCase,
1276 final);
1277 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1278 multiline,
1279 final);
1280 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1281 Smi::FromInt(0),
1282 writable);
1283 return regexp;
1284}
1285
1286
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001287static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1288 HandleScope scope;
1289 ASSERT(args.length() == 1);
1290 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1291 // This is necessary to enable fast checks for absence of elements
1292 // on Array.prototype and below.
1293 prototype->set_elements(Heap::empty_fixed_array());
1294 return Smi::FromInt(0);
1295}
1296
1297
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001298static void SetCustomCallGenerator(Handle<JSFunction> function,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001299 ExternalReference* generator) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001300 if (function->shared()->function_data()->IsUndefined()) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001301 function->shared()->set_function_data(*FromCData(generator->address()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001302 }
1303}
1304
1305
1306static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1307 const char* name,
1308 Builtins::Name builtin_name,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001309 ExternalReference* generator = NULL) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001310 Handle<String> key = Factory::LookupAsciiSymbol(name);
1311 Handle<Code> code(Builtins::builtin(builtin_name));
1312 Handle<JSFunction> optimized = Factory::NewFunction(key,
1313 JS_OBJECT_TYPE,
1314 JSObject::kHeaderSize,
1315 code,
1316 false);
1317 optimized->shared()->DontAdaptArguments();
1318 if (generator != NULL) {
1319 SetCustomCallGenerator(optimized, generator);
1320 }
1321 SetProperty(holder, key, optimized, NONE);
1322 return optimized;
1323}
1324
1325
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001326Object* CompileArrayPushCall(CallStubCompiler* compiler,
1327 Object* object,
1328 JSObject* holder,
1329 JSFunction* function,
1330 String* name,
1331 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001332 return compiler->CompileArrayPushCall(object, holder, function, name, check);
1333}
1334
1335
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001336Object* CompileArrayPopCall(CallStubCompiler* compiler,
1337 Object* object,
1338 JSObject* holder,
1339 JSFunction* function,
1340 String* name,
1341 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001342 return compiler->CompileArrayPopCall(object, holder, function, name, check);
1343}
1344
1345
1346static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1347 HandleScope scope;
1348 ASSERT(args.length() == 1);
1349 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1350
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001351 ExternalReference pop = ExternalReference::compile_array_pop_call();
1352 ExternalReference push = ExternalReference::compile_array_push_call();
1353
1354 InstallBuiltin(holder, "pop", Builtins::ArrayPop, &pop);
1355 InstallBuiltin(holder, "push", Builtins::ArrayPush, &push);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001356 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1357 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1358 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1359 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001360 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001361
1362 return *holder;
1363}
1364
1365
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001366static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1367 HandleScope scope;
1368 ASSERT(args.length() == 4);
1369 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1370 int index = Smi::cast(args[1])->value();
1371 Handle<String> pattern = args.at<String>(2);
1372 Handle<String> flags = args.at<String>(3);
1373
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001374 // Get the RegExp function from the context in the literals array.
1375 // This is the RegExp function from the context in which the
1376 // function was created. We do not use the RegExp function from the
1377 // current global context because this might be the RegExp function
1378 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001379 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001380 Handle<JSFunction>(
1381 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001382 // Compute the regular expression literal.
1383 bool has_pending_exception;
1384 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001385 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1386 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001387 if (has_pending_exception) {
1388 ASSERT(Top::has_pending_exception());
1389 return Failure::Exception();
1390 }
1391 literals->set(index, *regexp);
1392 return *regexp;
1393}
1394
1395
1396static Object* Runtime_FunctionGetName(Arguments args) {
1397 NoHandleAllocation ha;
1398 ASSERT(args.length() == 1);
1399
1400 CONVERT_CHECKED(JSFunction, f, args[0]);
1401 return f->shared()->name();
1402}
1403
1404
ager@chromium.org236ad962008-09-25 09:45:57 +00001405static Object* Runtime_FunctionSetName(Arguments args) {
1406 NoHandleAllocation ha;
1407 ASSERT(args.length() == 2);
1408
1409 CONVERT_CHECKED(JSFunction, f, args[0]);
1410 CONVERT_CHECKED(String, name, args[1]);
1411 f->shared()->set_name(name);
1412 return Heap::undefined_value();
1413}
1414
1415
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001416static Object* Runtime_FunctionGetScript(Arguments args) {
1417 HandleScope scope;
1418 ASSERT(args.length() == 1);
1419
1420 CONVERT_CHECKED(JSFunction, fun, args[0]);
1421 Handle<Object> script = Handle<Object>(fun->shared()->script());
1422 if (!script->IsScript()) return Heap::undefined_value();
1423
1424 return *GetScriptWrapper(Handle<Script>::cast(script));
1425}
1426
1427
1428static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1429 NoHandleAllocation ha;
1430 ASSERT(args.length() == 1);
1431
1432 CONVERT_CHECKED(JSFunction, f, args[0]);
1433 return f->shared()->GetSourceCode();
1434}
1435
1436
1437static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1438 NoHandleAllocation ha;
1439 ASSERT(args.length() == 1);
1440
1441 CONVERT_CHECKED(JSFunction, fun, args[0]);
1442 int pos = fun->shared()->start_position();
1443 return Smi::FromInt(pos);
1444}
1445
1446
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001447static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1448 ASSERT(args.length() == 2);
1449
1450 CONVERT_CHECKED(JSFunction, fun, args[0]);
1451 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1452
1453 Code* code = fun->code();
1454 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1455
1456 Address pc = code->address() + offset;
1457 return Smi::FromInt(fun->code()->SourcePosition(pc));
1458}
1459
1460
1461
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001462static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1463 NoHandleAllocation ha;
1464 ASSERT(args.length() == 2);
1465
1466 CONVERT_CHECKED(JSFunction, fun, args[0]);
1467 CONVERT_CHECKED(String, name, args[1]);
1468 fun->SetInstanceClassName(name);
1469 return Heap::undefined_value();
1470}
1471
1472
1473static Object* Runtime_FunctionSetLength(Arguments args) {
1474 NoHandleAllocation ha;
1475 ASSERT(args.length() == 2);
1476
1477 CONVERT_CHECKED(JSFunction, fun, args[0]);
1478 CONVERT_CHECKED(Smi, length, args[1]);
1479 fun->shared()->set_length(length->value());
1480 return length;
1481}
1482
1483
1484static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001485 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001486 ASSERT(args.length() == 2);
1487
1488 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001489 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1490 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001491 return args[0]; // return TOS
1492}
1493
1494
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001495static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1496 NoHandleAllocation ha;
1497 ASSERT(args.length() == 1);
1498
1499 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001500 return f->shared()->IsApiFunction() ? Heap::true_value()
1501 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001502}
1503
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001504static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1505 NoHandleAllocation ha;
1506 ASSERT(args.length() == 1);
1507
1508 CONVERT_CHECKED(JSFunction, f, args[0]);
1509 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1510}
1511
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001513static Object* Runtime_SetCode(Arguments args) {
1514 HandleScope scope;
1515 ASSERT(args.length() == 2);
1516
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001517 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001518 Handle<Object> code = args.at<Object>(1);
1519
1520 Handle<Context> context(target->context());
1521
1522 if (!code->IsNull()) {
1523 RUNTIME_ASSERT(code->IsJSFunction());
1524 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001525 Handle<SharedFunctionInfo> shared(fun->shared());
1526 SetExpectedNofProperties(target, shared->expected_nof_properties());
1527
1528 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001529 return Failure::Exception();
1530 }
1531 // Set the code, formal parameter count, and the length of the target
1532 // function.
1533 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001534 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001535 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001536 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001537 // Set the source code of the target function to undefined.
1538 // SetCode is only used for built-in constructors like String,
1539 // Array, and Object, and some web code
1540 // doesn't like seeing source code for constructors.
1541 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001542 // Clear the optimization hints related to the compiled code as these are no
1543 // longer valid when the code is overwritten.
1544 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001545 context = Handle<Context>(fun->context());
1546
1547 // Make sure we get a fresh copy of the literal vector to avoid
1548 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001549 int number_of_literals = fun->NumberOfLiterals();
1550 Handle<FixedArray> literals =
1551 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001552 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001553 // Insert the object, regexp and array functions in the literals
1554 // array prefix. These are the functions that will be used when
1555 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001556 literals->set(JSFunction::kLiteralGlobalContextIndex,
1557 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001558 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001559 // It's okay to skip the write barrier here because the literals
1560 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001561 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001562 }
1563
1564 target->set_context(*context);
1565 return *target;
1566}
1567
1568
1569static Object* CharCodeAt(String* subject, Object* index) {
1570 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001571 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001572 // Flatten the string. If someone wants to get a char at an index
1573 // in a cons string, it is likely that more indices will be
1574 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001575 Object* flat = subject->TryFlatten();
1576 if (flat->IsFailure()) return flat;
1577 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001578 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001579 return Heap::nan_value();
1580 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001581 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001582}
1583
1584
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001585static Object* CharFromCode(Object* char_code) {
1586 uint32_t code;
1587 if (Array::IndexFromObject(char_code, &code)) {
1588 if (code <= 0xffff) {
1589 return Heap::LookupSingleCharacterStringFromCode(code);
1590 }
1591 }
1592 return Heap::empty_string();
1593}
1594
1595
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001596static Object* Runtime_StringCharCodeAt(Arguments args) {
1597 NoHandleAllocation ha;
1598 ASSERT(args.length() == 2);
1599
1600 CONVERT_CHECKED(String, subject, args[0]);
1601 Object* index = args[1];
1602 return CharCodeAt(subject, index);
1603}
1604
1605
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001606static Object* Runtime_StringCharAt(Arguments args) {
1607 NoHandleAllocation ha;
1608 ASSERT(args.length() == 2);
1609
1610 CONVERT_CHECKED(String, subject, args[0]);
1611 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001612 Object* code = CharCodeAt(subject, index);
1613 if (code == Heap::nan_value()) {
1614 return Heap::undefined_value();
1615 }
1616 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001617}
1618
1619
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001620static Object* Runtime_CharFromCode(Arguments args) {
1621 NoHandleAllocation ha;
1622 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001623 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001624}
1625
lrn@chromium.org25156de2010-04-06 13:10:27 +00001626
1627class FixedArrayBuilder {
1628 public:
1629 explicit FixedArrayBuilder(int initial_capacity)
1630 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1631 length_(0) {
1632 // Require a non-zero initial size. Ensures that doubling the size to
1633 // extend the array will work.
1634 ASSERT(initial_capacity > 0);
1635 }
1636
1637 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1638 : array_(backing_store),
1639 length_(0) {
1640 // Require a non-zero initial size. Ensures that doubling the size to
1641 // extend the array will work.
1642 ASSERT(backing_store->length() > 0);
1643 }
1644
1645 bool HasCapacity(int elements) {
1646 int length = array_->length();
1647 int required_length = length_ + elements;
1648 return (length >= required_length);
1649 }
1650
1651 void EnsureCapacity(int elements) {
1652 int length = array_->length();
1653 int required_length = length_ + elements;
1654 if (length < required_length) {
1655 int new_length = length;
1656 do {
1657 new_length *= 2;
1658 } while (new_length < required_length);
1659 Handle<FixedArray> extended_array =
1660 Factory::NewFixedArrayWithHoles(new_length);
1661 array_->CopyTo(0, *extended_array, 0, length_);
1662 array_ = extended_array;
1663 }
1664 }
1665
1666 void Add(Object* value) {
1667 ASSERT(length_ < capacity());
1668 array_->set(length_, value);
1669 length_++;
1670 }
1671
1672 void Add(Smi* value) {
1673 ASSERT(length_ < capacity());
1674 array_->set(length_, value);
1675 length_++;
1676 }
1677
1678 Handle<FixedArray> array() {
1679 return array_;
1680 }
1681
1682 int length() {
1683 return length_;
1684 }
1685
1686 int capacity() {
1687 return array_->length();
1688 }
1689
1690 Handle<JSArray> ToJSArray() {
1691 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1692 result_array->set_length(Smi::FromInt(length_));
1693 return result_array;
1694 }
1695
1696 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1697 target_array->set_elements(*array_);
1698 target_array->set_length(Smi::FromInt(length_));
1699 return target_array;
1700 }
1701
1702 private:
1703 Handle<FixedArray> array_;
1704 int length_;
1705};
1706
1707
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001708// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001709const int kStringBuilderConcatHelperLengthBits = 11;
1710const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001711
1712template <typename schar>
1713static inline void StringBuilderConcatHelper(String*,
1714 schar*,
1715 FixedArray*,
1716 int);
1717
lrn@chromium.org25156de2010-04-06 13:10:27 +00001718typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1719 StringBuilderSubstringLength;
1720typedef BitField<int,
1721 kStringBuilderConcatHelperLengthBits,
1722 kStringBuilderConcatHelperPositionBits>
1723 StringBuilderSubstringPosition;
1724
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001725
1726class ReplacementStringBuilder {
1727 public:
1728 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001729 : array_builder_(estimated_part_count),
1730 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001731 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001732 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001733 // Require a non-zero initial size. Ensures that doubling the size to
1734 // extend the array will work.
1735 ASSERT(estimated_part_count > 0);
1736 }
1737
lrn@chromium.org25156de2010-04-06 13:10:27 +00001738 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1739 int from,
1740 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001741 ASSERT(from >= 0);
1742 int length = to - from;
1743 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001744 if (StringBuilderSubstringLength::is_valid(length) &&
1745 StringBuilderSubstringPosition::is_valid(from)) {
1746 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1747 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001748 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001749 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001750 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001751 builder->Add(Smi::FromInt(-length));
1752 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001753 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001754 }
1755
1756
1757 void EnsureCapacity(int elements) {
1758 array_builder_.EnsureCapacity(elements);
1759 }
1760
1761
1762 void AddSubjectSlice(int from, int to) {
1763 AddSubjectSlice(&array_builder_, from, to);
1764 // Can we encode the slice in 11 bits for length and 19 bits for
1765 // start position - as used by StringBuilderConcatHelper?
1766 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001767 }
1768
1769
1770 void AddString(Handle<String> string) {
1771 int length = string->length();
1772 ASSERT(length > 0);
1773 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001774 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001775 is_ascii_ = false;
1776 }
1777 IncrementCharacterCount(length);
1778 }
1779
1780
1781 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001782 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001783 return Factory::empty_string();
1784 }
1785
1786 Handle<String> joined_string;
1787 if (is_ascii_) {
1788 joined_string = NewRawAsciiString(character_count_);
1789 AssertNoAllocation no_alloc;
1790 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1791 char* char_buffer = seq->GetChars();
1792 StringBuilderConcatHelper(*subject_,
1793 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001794 *array_builder_.array(),
1795 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001796 } else {
1797 // Non-ASCII.
1798 joined_string = NewRawTwoByteString(character_count_);
1799 AssertNoAllocation no_alloc;
1800 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1801 uc16* char_buffer = seq->GetChars();
1802 StringBuilderConcatHelper(*subject_,
1803 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001804 *array_builder_.array(),
1805 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001806 }
1807 return joined_string;
1808 }
1809
1810
1811 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001812 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001813 V8::FatalProcessOutOfMemory("String.replace result too large.");
1814 }
1815 character_count_ += by;
1816 }
1817
lrn@chromium.org25156de2010-04-06 13:10:27 +00001818 Handle<JSArray> GetParts() {
1819 Handle<JSArray> result =
1820 Factory::NewJSArrayWithElements(array_builder_.array());
1821 result->set_length(Smi::FromInt(array_builder_.length()));
1822 return result;
1823 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001824
lrn@chromium.org25156de2010-04-06 13:10:27 +00001825 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001826 Handle<String> NewRawAsciiString(int size) {
1827 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1828 }
1829
1830
1831 Handle<String> NewRawTwoByteString(int size) {
1832 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1833 }
1834
1835
1836 void AddElement(Object* element) {
1837 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001838 ASSERT(array_builder_.capacity() > array_builder_.length());
1839 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001840 }
1841
lrn@chromium.org25156de2010-04-06 13:10:27 +00001842 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001843 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001844 int character_count_;
1845 bool is_ascii_;
1846};
1847
1848
1849class CompiledReplacement {
1850 public:
1851 CompiledReplacement()
1852 : parts_(1), replacement_substrings_(0) {}
1853
1854 void Compile(Handle<String> replacement,
1855 int capture_count,
1856 int subject_length);
1857
1858 void Apply(ReplacementStringBuilder* builder,
1859 int match_from,
1860 int match_to,
1861 Handle<JSArray> last_match_info);
1862
1863 // Number of distinct parts of the replacement pattern.
1864 int parts() {
1865 return parts_.length();
1866 }
1867 private:
1868 enum PartType {
1869 SUBJECT_PREFIX = 1,
1870 SUBJECT_SUFFIX,
1871 SUBJECT_CAPTURE,
1872 REPLACEMENT_SUBSTRING,
1873 REPLACEMENT_STRING,
1874
1875 NUMBER_OF_PART_TYPES
1876 };
1877
1878 struct ReplacementPart {
1879 static inline ReplacementPart SubjectMatch() {
1880 return ReplacementPart(SUBJECT_CAPTURE, 0);
1881 }
1882 static inline ReplacementPart SubjectCapture(int capture_index) {
1883 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1884 }
1885 static inline ReplacementPart SubjectPrefix() {
1886 return ReplacementPart(SUBJECT_PREFIX, 0);
1887 }
1888 static inline ReplacementPart SubjectSuffix(int subject_length) {
1889 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1890 }
1891 static inline ReplacementPart ReplacementString() {
1892 return ReplacementPart(REPLACEMENT_STRING, 0);
1893 }
1894 static inline ReplacementPart ReplacementSubString(int from, int to) {
1895 ASSERT(from >= 0);
1896 ASSERT(to > from);
1897 return ReplacementPart(-from, to);
1898 }
1899
1900 // If tag <= 0 then it is the negation of a start index of a substring of
1901 // the replacement pattern, otherwise it's a value from PartType.
1902 ReplacementPart(int tag, int data)
1903 : tag(tag), data(data) {
1904 // Must be non-positive or a PartType value.
1905 ASSERT(tag < NUMBER_OF_PART_TYPES);
1906 }
1907 // Either a value of PartType or a non-positive number that is
1908 // the negation of an index into the replacement string.
1909 int tag;
1910 // The data value's interpretation depends on the value of tag:
1911 // tag == SUBJECT_PREFIX ||
1912 // tag == SUBJECT_SUFFIX: data is unused.
1913 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1914 // tag == REPLACEMENT_SUBSTRING ||
1915 // tag == REPLACEMENT_STRING: data is index into array of substrings
1916 // of the replacement string.
1917 // tag <= 0: Temporary representation of the substring of the replacement
1918 // string ranging over -tag .. data.
1919 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1920 // substring objects.
1921 int data;
1922 };
1923
1924 template<typename Char>
1925 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1926 Vector<Char> characters,
1927 int capture_count,
1928 int subject_length) {
1929 int length = characters.length();
1930 int last = 0;
1931 for (int i = 0; i < length; i++) {
1932 Char c = characters[i];
1933 if (c == '$') {
1934 int next_index = i + 1;
1935 if (next_index == length) { // No next character!
1936 break;
1937 }
1938 Char c2 = characters[next_index];
1939 switch (c2) {
1940 case '$':
1941 if (i > last) {
1942 // There is a substring before. Include the first "$".
1943 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1944 last = next_index + 1; // Continue after the second "$".
1945 } else {
1946 // Let the next substring start with the second "$".
1947 last = next_index;
1948 }
1949 i = next_index;
1950 break;
1951 case '`':
1952 if (i > last) {
1953 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1954 }
1955 parts->Add(ReplacementPart::SubjectPrefix());
1956 i = next_index;
1957 last = i + 1;
1958 break;
1959 case '\'':
1960 if (i > last) {
1961 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1962 }
1963 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1964 i = next_index;
1965 last = i + 1;
1966 break;
1967 case '&':
1968 if (i > last) {
1969 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1970 }
1971 parts->Add(ReplacementPart::SubjectMatch());
1972 i = next_index;
1973 last = i + 1;
1974 break;
1975 case '0':
1976 case '1':
1977 case '2':
1978 case '3':
1979 case '4':
1980 case '5':
1981 case '6':
1982 case '7':
1983 case '8':
1984 case '9': {
1985 int capture_ref = c2 - '0';
1986 if (capture_ref > capture_count) {
1987 i = next_index;
1988 continue;
1989 }
1990 int second_digit_index = next_index + 1;
1991 if (second_digit_index < length) {
1992 // Peek ahead to see if we have two digits.
1993 Char c3 = characters[second_digit_index];
1994 if ('0' <= c3 && c3 <= '9') { // Double digits.
1995 int double_digit_ref = capture_ref * 10 + c3 - '0';
1996 if (double_digit_ref <= capture_count) {
1997 next_index = second_digit_index;
1998 capture_ref = double_digit_ref;
1999 }
2000 }
2001 }
2002 if (capture_ref > 0) {
2003 if (i > last) {
2004 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2005 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002006 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002007 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2008 last = next_index + 1;
2009 }
2010 i = next_index;
2011 break;
2012 }
2013 default:
2014 i = next_index;
2015 break;
2016 }
2017 }
2018 }
2019 if (length > last) {
2020 if (last == 0) {
2021 parts->Add(ReplacementPart::ReplacementString());
2022 } else {
2023 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2024 }
2025 }
2026 }
2027
2028 ZoneList<ReplacementPart> parts_;
2029 ZoneList<Handle<String> > replacement_substrings_;
2030};
2031
2032
2033void CompiledReplacement::Compile(Handle<String> replacement,
2034 int capture_count,
2035 int subject_length) {
2036 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002037 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002038 AssertNoAllocation no_alloc;
2039 ParseReplacementPattern(&parts_,
2040 replacement->ToAsciiVector(),
2041 capture_count,
2042 subject_length);
2043 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002044 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002045 AssertNoAllocation no_alloc;
2046
2047 ParseReplacementPattern(&parts_,
2048 replacement->ToUC16Vector(),
2049 capture_count,
2050 subject_length);
2051 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002052 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002053 int substring_index = 0;
2054 for (int i = 0, n = parts_.length(); i < n; i++) {
2055 int tag = parts_[i].tag;
2056 if (tag <= 0) { // A replacement string slice.
2057 int from = -tag;
2058 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002059 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002060 parts_[i].tag = REPLACEMENT_SUBSTRING;
2061 parts_[i].data = substring_index;
2062 substring_index++;
2063 } else if (tag == REPLACEMENT_STRING) {
2064 replacement_substrings_.Add(replacement);
2065 parts_[i].data = substring_index;
2066 substring_index++;
2067 }
2068 }
2069}
2070
2071
2072void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2073 int match_from,
2074 int match_to,
2075 Handle<JSArray> last_match_info) {
2076 for (int i = 0, n = parts_.length(); i < n; i++) {
2077 ReplacementPart part = parts_[i];
2078 switch (part.tag) {
2079 case SUBJECT_PREFIX:
2080 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2081 break;
2082 case SUBJECT_SUFFIX: {
2083 int subject_length = part.data;
2084 if (match_to < subject_length) {
2085 builder->AddSubjectSlice(match_to, subject_length);
2086 }
2087 break;
2088 }
2089 case SUBJECT_CAPTURE: {
2090 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002091 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002092 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2093 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2094 if (from >= 0 && to > from) {
2095 builder->AddSubjectSlice(from, to);
2096 }
2097 break;
2098 }
2099 case REPLACEMENT_SUBSTRING:
2100 case REPLACEMENT_STRING:
2101 builder->AddString(replacement_substrings_[part.data]);
2102 break;
2103 default:
2104 UNREACHABLE();
2105 }
2106 }
2107}
2108
2109
2110
2111static Object* StringReplaceRegExpWithString(String* subject,
2112 JSRegExp* regexp,
2113 String* replacement,
2114 JSArray* last_match_info) {
2115 ASSERT(subject->IsFlat());
2116 ASSERT(replacement->IsFlat());
2117
2118 HandleScope handles;
2119
2120 int length = subject->length();
2121 Handle<String> subject_handle(subject);
2122 Handle<JSRegExp> regexp_handle(regexp);
2123 Handle<String> replacement_handle(replacement);
2124 Handle<JSArray> last_match_info_handle(last_match_info);
2125 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2126 subject_handle,
2127 0,
2128 last_match_info_handle);
2129 if (match.is_null()) {
2130 return Failure::Exception();
2131 }
2132 if (match->IsNull()) {
2133 return *subject_handle;
2134 }
2135
2136 int capture_count = regexp_handle->CaptureCount();
2137
2138 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002139 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002140 CompiledReplacement compiled_replacement;
2141 compiled_replacement.Compile(replacement_handle,
2142 capture_count,
2143 length);
2144
2145 bool is_global = regexp_handle->GetFlags().is_global();
2146
2147 // Guessing the number of parts that the final result string is built
2148 // from. Global regexps can match any number of times, so we guess
2149 // conservatively.
2150 int expected_parts =
2151 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2152 ReplacementStringBuilder builder(subject_handle, expected_parts);
2153
2154 // Index of end of last match.
2155 int prev = 0;
2156
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002157 // Number of parts added by compiled replacement plus preceeding
2158 // string and possibly suffix after last match. It is possible for
2159 // all components to use two elements when encoded as two smis.
2160 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002161 bool matched = true;
2162 do {
2163 ASSERT(last_match_info_handle->HasFastElements());
2164 // Increase the capacity of the builder before entering local handle-scope,
2165 // so its internal buffer can safely allocate a new handle if it grows.
2166 builder.EnsureCapacity(parts_added_per_loop);
2167
2168 HandleScope loop_scope;
2169 int start, end;
2170 {
2171 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002172 FixedArray* match_info_array =
2173 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002174
2175 ASSERT_EQ(capture_count * 2 + 2,
2176 RegExpImpl::GetLastCaptureCount(match_info_array));
2177 start = RegExpImpl::GetCapture(match_info_array, 0);
2178 end = RegExpImpl::GetCapture(match_info_array, 1);
2179 }
2180
2181 if (prev < start) {
2182 builder.AddSubjectSlice(prev, start);
2183 }
2184 compiled_replacement.Apply(&builder,
2185 start,
2186 end,
2187 last_match_info_handle);
2188 prev = end;
2189
2190 // Only continue checking for global regexps.
2191 if (!is_global) break;
2192
2193 // Continue from where the match ended, unless it was an empty match.
2194 int next = end;
2195 if (start == end) {
2196 next = end + 1;
2197 if (next > length) break;
2198 }
2199
2200 match = RegExpImpl::Exec(regexp_handle,
2201 subject_handle,
2202 next,
2203 last_match_info_handle);
2204 if (match.is_null()) {
2205 return Failure::Exception();
2206 }
2207 matched = !match->IsNull();
2208 } while (matched);
2209
2210 if (prev < length) {
2211 builder.AddSubjectSlice(prev, length);
2212 }
2213
2214 return *(builder.ToString());
2215}
2216
2217
2218static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2219 ASSERT(args.length() == 4);
2220
2221 CONVERT_CHECKED(String, subject, args[0]);
2222 if (!subject->IsFlat()) {
2223 Object* flat_subject = subject->TryFlatten();
2224 if (flat_subject->IsFailure()) {
2225 return flat_subject;
2226 }
2227 subject = String::cast(flat_subject);
2228 }
2229
2230 CONVERT_CHECKED(String, replacement, args[2]);
2231 if (!replacement->IsFlat()) {
2232 Object* flat_replacement = replacement->TryFlatten();
2233 if (flat_replacement->IsFailure()) {
2234 return flat_replacement;
2235 }
2236 replacement = String::cast(flat_replacement);
2237 }
2238
2239 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2240 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2241
2242 ASSERT(last_match_info->HasFastElements());
2243
2244 return StringReplaceRegExpWithString(subject,
2245 regexp,
2246 replacement,
2247 last_match_info);
2248}
2249
2250
ager@chromium.org7c537e22008-10-16 08:43:32 +00002251// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2252// limit, we can fix the size of tables.
2253static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002254// Reduce alphabet to this size.
2255static const int kBMAlphabetSize = 0x100;
2256// For patterns below this length, the skip length of Boyer-Moore is too short
2257// to compensate for the algorithmic overhead compared to simple brute force.
2258static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002259
ager@chromium.org7c537e22008-10-16 08:43:32 +00002260// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2261// shift. Only allows the last kBMMaxShift characters of the needle
2262// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002263class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002264 public:
2265 BMGoodSuffixBuffers() {}
2266 inline void init(int needle_length) {
2267 ASSERT(needle_length > 1);
2268 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2269 int len = needle_length - start;
2270 biased_suffixes_ = suffixes_ - start;
2271 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2272 for (int i = 0; i <= len; i++) {
2273 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002274 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002275 }
2276 inline int& suffix(int index) {
2277 ASSERT(biased_suffixes_ + index >= suffixes_);
2278 return biased_suffixes_[index];
2279 }
2280 inline int& shift(int index) {
2281 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2282 return biased_good_suffix_shift_[index];
2283 }
2284 private:
2285 int suffixes_[kBMMaxShift + 1];
2286 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002287 int* biased_suffixes_;
2288 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002289 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2290};
2291
2292// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002293static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002294static BMGoodSuffixBuffers bmgs_buffers;
2295
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002296// State of the string match tables.
2297// SIMPLE: No usable content in the buffers.
2298// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2299// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2300// Whenever starting with a new needle, one should call InitializeStringSearch
2301// to determine which search strategy to use, and in the case of a long-needle
2302// strategy, the call also initializes the algorithm to SIMPLE.
2303enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2304static StringSearchAlgorithm algorithm;
2305
2306
ager@chromium.org7c537e22008-10-16 08:43:32 +00002307// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002308template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002309static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2310 // Only preprocess at most kBMMaxShift last characters of pattern.
2311 int start = pattern.length() < kBMMaxShift ? 0
2312 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002313 // Run forwards to populate bad_char_table, so that *last* instance
2314 // of character equivalence class is the one registered.
2315 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002316 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2317 : kBMAlphabetSize;
2318 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002319 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002320 } else {
2321 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002322 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002323 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002324 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002325 for (int i = start; i < pattern.length() - 1; i++) {
2326 pchar c = pattern[i];
2327 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002328 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002329 }
2330}
2331
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002332
ager@chromium.org7c537e22008-10-16 08:43:32 +00002333template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002334static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002335 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002336 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002337 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002338 // Compute Good Suffix tables.
2339 bmgs_buffers.init(m);
2340
2341 bmgs_buffers.shift(m-1) = 1;
2342 bmgs_buffers.suffix(m) = m + 1;
2343 pchar last_char = pattern[m - 1];
2344 int suffix = m + 1;
2345 for (int i = m; i > start;) {
2346 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2347 if (bmgs_buffers.shift(suffix) == len) {
2348 bmgs_buffers.shift(suffix) = suffix - i;
2349 }
2350 suffix = bmgs_buffers.suffix(suffix);
2351 }
2352 i--;
2353 suffix--;
2354 bmgs_buffers.suffix(i) = suffix;
2355 if (suffix == m) {
2356 // No suffix to extend, so we check against last_char only.
2357 while (i > start && pattern[i - 1] != last_char) {
2358 if (bmgs_buffers.shift(m) == len) {
2359 bmgs_buffers.shift(m) = m - i;
2360 }
2361 i--;
2362 bmgs_buffers.suffix(i) = m;
2363 }
2364 if (i > start) {
2365 i--;
2366 suffix--;
2367 bmgs_buffers.suffix(i) = suffix;
2368 }
2369 }
2370 }
2371 if (suffix < m) {
2372 for (int i = start; i <= m; i++) {
2373 if (bmgs_buffers.shift(i) == len) {
2374 bmgs_buffers.shift(i) = suffix - start;
2375 }
2376 if (i == suffix) {
2377 suffix = bmgs_buffers.suffix(suffix);
2378 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002379 }
2380 }
2381}
2382
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002383
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002384template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002385static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002386 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002387 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002388 }
2389 if (sizeof(pchar) == 1) {
2390 if (char_code > String::kMaxAsciiCharCode) {
2391 return -1;
2392 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002393 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002394 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002395 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002396}
2397
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002398
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002399// Restricted simplified Boyer-Moore string matching.
2400// Uses only the bad-shift table of Boyer-Moore and only uses it
2401// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002402template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002403static int BoyerMooreHorspool(Vector<const schar> subject,
2404 Vector<const pchar> pattern,
2405 int start_index,
2406 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002407 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002408 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002409 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002411 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002412
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002413 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002414 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002415 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002416 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002417 // Perform search
2418 for (idx = start_index; idx <= n - m;) {
2419 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002420 int c;
2421 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002422 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002423 int shift = j - bc_occ;
2424 idx += shift;
2425 badness += 1 - shift; // at most zero, so badness cannot increase.
2426 if (idx > n - m) {
2427 *complete = true;
2428 return -1;
2429 }
2430 }
2431 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002432 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002433 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002434 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002435 return idx;
2436 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002437 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002438 // Badness increases by the number of characters we have
2439 // checked, and decreases by the number of characters we
2440 // can skip by shifting. It's a measure of how we are doing
2441 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002442 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002443 if (badness > 0) {
2444 *complete = false;
2445 return idx;
2446 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002447 }
2448 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002449 *complete = true;
2450 return -1;
2451}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002452
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002453
2454template <typename schar, typename pchar>
2455static int BoyerMooreIndexOf(Vector<const schar> subject,
2456 Vector<const pchar> pattern,
2457 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002458 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002459 int n = subject.length();
2460 int m = pattern.length();
2461 // Only preprocess at most kBMMaxShift last characters of pattern.
2462 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2463
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002464 pchar last_char = pattern[m - 1];
2465 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002466 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002467 int j = m - 1;
2468 schar c;
2469 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002470 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002471 idx += shift;
2472 if (idx > n - m) {
2473 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002474 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002475 }
2476 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2477 if (j < 0) {
2478 return idx;
2479 } else if (j < start) {
2480 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002481 // Fall back on BMH shift.
2482 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002483 } else {
2484 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002485 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002486 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002487 if (gs_shift > shift) {
2488 shift = gs_shift;
2489 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002490 idx += shift;
2491 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002492 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002493
2494 return -1;
2495}
2496
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002497
2498template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002499static inline int SingleCharIndexOf(Vector<const schar> string,
2500 schar pattern_char,
2501 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002502 if (sizeof(schar) == 1) {
2503 const schar* pos = reinterpret_cast<const schar*>(
2504 memchr(string.start() + start_index,
2505 pattern_char,
2506 string.length() - start_index));
2507 if (pos == NULL) return -1;
2508 return pos - string.start();
2509 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002510 for (int i = start_index, n = string.length(); i < n; i++) {
2511 if (pattern_char == string[i]) {
2512 return i;
2513 }
2514 }
2515 return -1;
2516}
2517
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002518
2519template <typename schar>
2520static int SingleCharLastIndexOf(Vector<const schar> string,
2521 schar pattern_char,
2522 int start_index) {
2523 for (int i = start_index; i >= 0; i--) {
2524 if (pattern_char == string[i]) {
2525 return i;
2526 }
2527 }
2528 return -1;
2529}
2530
2531
ager@chromium.org7c537e22008-10-16 08:43:32 +00002532// Trivial string search for shorter strings.
2533// On return, if "complete" is set to true, the return value is the
2534// final result of searching for the patter in the subject.
2535// If "complete" is set to false, the return value is the index where
2536// further checking should start, i.e., it's guaranteed that the pattern
2537// does not occur at a position prior to the returned index.
2538template <typename pchar, typename schar>
2539static int SimpleIndexOf(Vector<const schar> subject,
2540 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002541 int idx,
2542 bool* complete) {
2543 // Badness is a count of how much work we have done. When we have
2544 // done enough work we decide it's probably worth switching to a better
2545 // algorithm.
2546 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002547
ager@chromium.org7c537e22008-10-16 08:43:32 +00002548 // We know our pattern is at least 2 characters, we cache the first so
2549 // the common case of the first character not matching is faster.
2550 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002551 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2552 badness++;
2553 if (badness > 0) {
2554 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002555 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002556 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002557 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2558 const schar* pos = reinterpret_cast<const schar*>(
2559 memchr(subject.start() + i,
2560 pattern_first_char,
2561 n - i + 1));
2562 if (pos == NULL) {
2563 *complete = true;
2564 return -1;
2565 }
2566 i = pos - subject.start();
2567 } else {
2568 if (subject[i] != pattern_first_char) continue;
2569 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002570 int j = 1;
2571 do {
2572 if (pattern[j] != subject[i+j]) {
2573 break;
2574 }
2575 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002576 } while (j < pattern.length());
2577 if (j == pattern.length()) {
2578 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002579 return i;
2580 }
2581 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002582 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002583 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002584 return -1;
2585}
2586
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002587// Simple indexOf that never bails out. For short patterns only.
2588template <typename pchar, typename schar>
2589static int SimpleIndexOf(Vector<const schar> subject,
2590 Vector<const pchar> pattern,
2591 int idx) {
2592 pchar pattern_first_char = pattern[0];
2593 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002594 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2595 const schar* pos = reinterpret_cast<const schar*>(
2596 memchr(subject.start() + i,
2597 pattern_first_char,
2598 n - i + 1));
2599 if (pos == NULL) return -1;
2600 i = pos - subject.start();
2601 } else {
2602 if (subject[i] != pattern_first_char) continue;
2603 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002604 int j = 1;
2605 do {
2606 if (pattern[j] != subject[i+j]) {
2607 break;
2608 }
2609 j++;
2610 } while (j < pattern.length());
2611 if (j == pattern.length()) {
2612 return i;
2613 }
2614 }
2615 return -1;
2616}
2617
2618
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002619// Strategy for searching for a string in another string.
2620enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002621
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002622
2623template <typename pchar>
2624static inline StringSearchStrategy InitializeStringSearch(
2625 Vector<const pchar> pat, bool ascii_subject) {
2626 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002627 // We have an ASCII haystack and a non-ASCII needle. Check if there
2628 // really is a non-ASCII character in the needle and bail out if there
2629 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002630 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002631 for (int i = 0; i < pat.length(); i++) {
2632 uc16 c = pat[i];
2633 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002634 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002635 }
2636 }
2637 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002638 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002639 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002640 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002641 algorithm = SIMPLE_SEARCH;
2642 return SEARCH_LONG;
2643}
2644
2645
2646// Dispatch long needle searches to different algorithms.
2647template <typename schar, typename pchar>
2648static int ComplexIndexOf(Vector<const schar> sub,
2649 Vector<const pchar> pat,
2650 int start_index) {
2651 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002652 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002653 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002654 int idx = start_index;
2655 switch (algorithm) {
2656 case SIMPLE_SEARCH:
2657 idx = SimpleIndexOf(sub, pat, idx, &complete);
2658 if (complete) return idx;
2659 BoyerMoorePopulateBadCharTable(pat);
2660 algorithm = BOYER_MOORE_HORSPOOL;
2661 // FALLTHROUGH.
2662 case BOYER_MOORE_HORSPOOL:
2663 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2664 if (complete) return idx;
2665 // Build the Good Suffix table and continue searching.
2666 BoyerMoorePopulateGoodSuffixTable(pat);
2667 algorithm = BOYER_MOORE;
2668 // FALLTHROUGH.
2669 case BOYER_MOORE:
2670 return BoyerMooreIndexOf(sub, pat, idx);
2671 }
2672 UNREACHABLE();
2673 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002674}
2675
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002676
2677// Dispatch to different search strategies for a single search.
2678// If searching multiple times on the same needle, the search
2679// strategy should only be computed once and then dispatch to different
2680// loops.
2681template <typename schar, typename pchar>
2682static int StringSearch(Vector<const schar> sub,
2683 Vector<const pchar> pat,
2684 int start_index) {
2685 bool ascii_subject = (sizeof(schar) == 1);
2686 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2687 switch (strategy) {
2688 case SEARCH_FAIL: return -1;
2689 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2690 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2691 }
2692 UNREACHABLE();
2693 return -1;
2694}
2695
2696
ager@chromium.org7c537e22008-10-16 08:43:32 +00002697// Perform string match of pattern on subject, starting at start index.
2698// Caller must ensure that 0 <= start_index <= sub->length(),
2699// and should check that pat->length() + start_index <= sub->length()
2700int Runtime::StringMatch(Handle<String> sub,
2701 Handle<String> pat,
2702 int start_index) {
2703 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002704 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002705
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002706 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002707 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002709 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002710 if (start_index + pattern_length > subject_length) return -1;
2711
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002712 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002713 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002714 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002715
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002716 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002717 // character patterns linear search is necessary, so any smart
2718 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002719 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002720 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002721 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002722 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002723 if (pchar > String::kMaxAsciiCharCode) {
2724 return -1;
2725 }
2726 Vector<const char> ascii_vector =
2727 sub->ToAsciiVector().SubVector(start_index, subject_length);
2728 const void* pos = memchr(ascii_vector.start(),
2729 static_cast<const char>(pchar),
2730 static_cast<size_t>(ascii_vector.length()));
2731 if (pos == NULL) {
2732 return -1;
2733 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002734 return static_cast<int>(reinterpret_cast<const char*>(pos)
2735 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002736 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002737 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002738 }
2739
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002740 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002741 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002742 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002743
ager@chromium.org7c537e22008-10-16 08:43:32 +00002744 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2745 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002746 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002747 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002748 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002749 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002750 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002751 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002752 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002753 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002754 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002755 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002756 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002757 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002758}
2759
2760
2761static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002762 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002763 ASSERT(args.length() == 3);
2764
ager@chromium.org7c537e22008-10-16 08:43:32 +00002765 CONVERT_ARG_CHECKED(String, sub, 0);
2766 CONVERT_ARG_CHECKED(String, pat, 1);
2767
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002768 Object* index = args[2];
2769 uint32_t start_index;
2770 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2771
ager@chromium.org870a0b62008-11-04 11:43:05 +00002772 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002773 int position = Runtime::StringMatch(sub, pat, start_index);
2774 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002775}
2776
2777
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002778template <typename schar, typename pchar>
2779static int StringMatchBackwards(Vector<const schar> sub,
2780 Vector<const pchar> pat,
2781 int idx) {
2782 ASSERT(pat.length() >= 1);
2783 ASSERT(idx + pat.length() <= sub.length());
2784
2785 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2786 for (int i = 0; i < pat.length(); i++) {
2787 uc16 c = pat[i];
2788 if (c > String::kMaxAsciiCharCode) {
2789 return -1;
2790 }
2791 }
2792 }
2793
2794 pchar pattern_first_char = pat[0];
2795 for (int i = idx; i >= 0; i--) {
2796 if (sub[i] != pattern_first_char) continue;
2797 int j = 1;
2798 while (j < pat.length()) {
2799 if (pat[j] != sub[i+j]) {
2800 break;
2801 }
2802 j++;
2803 }
2804 if (j == pat.length()) {
2805 return i;
2806 }
2807 }
2808 return -1;
2809}
2810
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002811static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002812 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002813 ASSERT(args.length() == 3);
2814
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002815 CONVERT_ARG_CHECKED(String, sub, 0);
2816 CONVERT_ARG_CHECKED(String, pat, 1);
2817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002818 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002819 uint32_t start_index;
2820 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2821
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002822 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002823 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002824
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002825 if (start_index + pat_length > sub_length) {
2826 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002827 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002828
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002829 if (pat_length == 0) {
2830 return Smi::FromInt(start_index);
2831 }
2832
2833 if (!sub->IsFlat()) {
2834 FlattenString(sub);
2835 }
2836
2837 if (pat_length == 1) {
2838 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2839 if (sub->IsAsciiRepresentation()) {
2840 uc16 pchar = pat->Get(0);
2841 if (pchar > String::kMaxAsciiCharCode) {
2842 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002843 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002844 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2845 static_cast<char>(pat->Get(0)),
2846 start_index));
2847 } else {
2848 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2849 pat->Get(0),
2850 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002851 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002852 }
2853
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002854 if (!pat->IsFlat()) {
2855 FlattenString(pat);
2856 }
2857
2858 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2859
2860 int position = -1;
2861
2862 if (pat->IsAsciiRepresentation()) {
2863 Vector<const char> pat_vector = pat->ToAsciiVector();
2864 if (sub->IsAsciiRepresentation()) {
2865 position = StringMatchBackwards(sub->ToAsciiVector(),
2866 pat_vector,
2867 start_index);
2868 } else {
2869 position = StringMatchBackwards(sub->ToUC16Vector(),
2870 pat_vector,
2871 start_index);
2872 }
2873 } else {
2874 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2875 if (sub->IsAsciiRepresentation()) {
2876 position = StringMatchBackwards(sub->ToAsciiVector(),
2877 pat_vector,
2878 start_index);
2879 } else {
2880 position = StringMatchBackwards(sub->ToUC16Vector(),
2881 pat_vector,
2882 start_index);
2883 }
2884 }
2885
2886 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887}
2888
2889
2890static Object* Runtime_StringLocaleCompare(Arguments args) {
2891 NoHandleAllocation ha;
2892 ASSERT(args.length() == 2);
2893
2894 CONVERT_CHECKED(String, str1, args[0]);
2895 CONVERT_CHECKED(String, str2, args[1]);
2896
2897 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002898 int str1_length = str1->length();
2899 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002900
2901 // Decide trivial cases without flattening.
2902 if (str1_length == 0) {
2903 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2904 return Smi::FromInt(-str2_length);
2905 } else {
2906 if (str2_length == 0) return Smi::FromInt(str1_length);
2907 }
2908
2909 int end = str1_length < str2_length ? str1_length : str2_length;
2910
2911 // No need to flatten if we are going to find the answer on the first
2912 // character. At this point we know there is at least one character
2913 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002914 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915 if (d != 0) return Smi::FromInt(d);
2916
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002917 str1->TryFlatten();
2918 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002919
2920 static StringInputBuffer buf1;
2921 static StringInputBuffer buf2;
2922
2923 buf1.Reset(str1);
2924 buf2.Reset(str2);
2925
2926 for (int i = 0; i < end; i++) {
2927 uint16_t char1 = buf1.GetNext();
2928 uint16_t char2 = buf2.GetNext();
2929 if (char1 != char2) return Smi::FromInt(char1 - char2);
2930 }
2931
2932 return Smi::FromInt(str1_length - str2_length);
2933}
2934
2935
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002936static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002937 NoHandleAllocation ha;
2938 ASSERT(args.length() == 3);
2939
2940 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002941 Object* from = args[1];
2942 Object* to = args[2];
2943 int start, end;
2944 // We have a fast integer-only case here to avoid a conversion to double in
2945 // the common case where from and to are Smis.
2946 if (from->IsSmi() && to->IsSmi()) {
2947 start = Smi::cast(from)->value();
2948 end = Smi::cast(to)->value();
2949 } else {
2950 CONVERT_DOUBLE_CHECKED(from_number, from);
2951 CONVERT_DOUBLE_CHECKED(to_number, to);
2952 start = FastD2I(from_number);
2953 end = FastD2I(to_number);
2954 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002955 RUNTIME_ASSERT(end >= start);
2956 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002957 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002958 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002959 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002960}
2961
2962
ager@chromium.org41826e72009-03-30 13:30:57 +00002963static Object* Runtime_StringMatch(Arguments args) {
2964 ASSERT_EQ(3, args.length());
2965
2966 CONVERT_ARG_CHECKED(String, subject, 0);
2967 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2968 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2969 HandleScope handles;
2970
2971 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2972
2973 if (match.is_null()) {
2974 return Failure::Exception();
2975 }
2976 if (match->IsNull()) {
2977 return Heap::null_value();
2978 }
2979 int length = subject->length();
2980
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002981 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002982 ZoneList<int> offsets(8);
2983 do {
2984 int start;
2985 int end;
2986 {
2987 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002988 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002989 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2990 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2991 }
2992 offsets.Add(start);
2993 offsets.Add(end);
2994 int index = start < end ? end : end + 1;
2995 if (index > length) break;
2996 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2997 if (match.is_null()) {
2998 return Failure::Exception();
2999 }
3000 } while (!match->IsNull());
3001 int matches = offsets.length() / 2;
3002 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3003 for (int i = 0; i < matches ; i++) {
3004 int from = offsets.at(i * 2);
3005 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003006 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003007 }
3008 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3009 result->set_length(Smi::FromInt(matches));
3010 return *result;
3011}
3012
3013
lrn@chromium.org25156de2010-04-06 13:10:27 +00003014// Two smis before and after the match, for very long strings.
3015const int kMaxBuilderEntriesPerRegExpMatch = 5;
3016
3017
3018static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3019 Handle<JSArray> last_match_info,
3020 int match_start,
3021 int match_end) {
3022 // Fill last_match_info with a single capture.
3023 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3024 AssertNoAllocation no_gc;
3025 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3026 RegExpImpl::SetLastCaptureCount(elements, 2);
3027 RegExpImpl::SetLastInput(elements, *subject);
3028 RegExpImpl::SetLastSubject(elements, *subject);
3029 RegExpImpl::SetCapture(elements, 0, match_start);
3030 RegExpImpl::SetCapture(elements, 1, match_end);
3031}
3032
3033
3034template <typename schar>
3035static bool SearchCharMultiple(Vector<schar> subject,
3036 String* pattern,
3037 schar pattern_char,
3038 FixedArrayBuilder* builder,
3039 int* match_pos) {
3040 // Position of last match.
3041 int pos = *match_pos;
3042 int subject_length = subject.length();
3043 while (pos < subject_length) {
3044 int match_end = pos + 1;
3045 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3046 *match_pos = pos;
3047 return false;
3048 }
3049 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3050 if (new_pos >= 0) {
3051 // Match has been found.
3052 if (new_pos > match_end) {
3053 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3054 }
3055 pos = new_pos;
3056 builder->Add(pattern);
3057 } else {
3058 break;
3059 }
3060 }
3061 if (pos + 1 < subject_length) {
3062 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3063 }
3064 *match_pos = pos;
3065 return true;
3066}
3067
3068
3069static bool SearchCharMultiple(Handle<String> subject,
3070 Handle<String> pattern,
3071 Handle<JSArray> last_match_info,
3072 FixedArrayBuilder* builder) {
3073 ASSERT(subject->IsFlat());
3074 ASSERT_EQ(1, pattern->length());
3075 uc16 pattern_char = pattern->Get(0);
3076 // Treating position before first as initial "previous match position".
3077 int match_pos = -1;
3078
3079 for (;;) { // Break when search complete.
3080 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3081 AssertNoAllocation no_gc;
3082 if (subject->IsAsciiRepresentation()) {
3083 if (pattern_char > String::kMaxAsciiCharCode) {
3084 break;
3085 }
3086 Vector<const char> subject_vector = subject->ToAsciiVector();
3087 char pattern_ascii_char = static_cast<char>(pattern_char);
3088 bool complete = SearchCharMultiple<const char>(subject_vector,
3089 *pattern,
3090 pattern_ascii_char,
3091 builder,
3092 &match_pos);
3093 if (complete) break;
3094 } else {
3095 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3096 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3097 *pattern,
3098 pattern_char,
3099 builder,
3100 &match_pos);
3101 if (complete) break;
3102 }
3103 }
3104
3105 if (match_pos >= 0) {
3106 SetLastMatchInfoNoCaptures(subject,
3107 last_match_info,
3108 match_pos,
3109 match_pos + 1);
3110 return true;
3111 }
3112 return false; // No matches at all.
3113}
3114
3115
3116template <typename schar, typename pchar>
3117static bool SearchStringMultiple(Vector<schar> subject,
3118 String* pattern,
3119 Vector<pchar> pattern_string,
3120 FixedArrayBuilder* builder,
3121 int* match_pos) {
3122 int pos = *match_pos;
3123 int subject_length = subject.length();
3124 int pattern_length = pattern_string.length();
3125 int max_search_start = subject_length - pattern_length;
3126 bool is_ascii = (sizeof(schar) == 1);
3127 StringSearchStrategy strategy =
3128 InitializeStringSearch(pattern_string, is_ascii);
3129 switch (strategy) {
3130 case SEARCH_FAIL: return false;
3131 case SEARCH_SHORT:
3132 while (pos <= max_search_start) {
3133 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3134 *match_pos = pos;
3135 return false;
3136 }
3137 // Position of end of previous match.
3138 int match_end = pos + pattern_length;
3139 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3140 if (new_pos >= 0) {
3141 // A match.
3142 if (new_pos > match_end) {
3143 ReplacementStringBuilder::AddSubjectSlice(builder,
3144 match_end,
3145 new_pos);
3146 }
3147 pos = new_pos;
3148 builder->Add(pattern);
3149 } else {
3150 break;
3151 }
3152 }
3153 break;
3154 case SEARCH_LONG:
3155 while (pos <= max_search_start) {
3156 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3157 *match_pos = pos;
3158 return false;
3159 }
3160 int new_pos = ComplexIndexOf(subject,
3161 pattern_string,
3162 pos + pattern_length);
3163 if (new_pos >= 0) {
3164 // A match has been found.
3165 if (new_pos > pos) {
3166 ReplacementStringBuilder::AddSubjectSlice(builder, pos, new_pos);
3167 }
3168 pos = new_pos;
3169 builder->Add(pattern);
3170 } else {
3171 break;
3172 }
3173 }
3174 break;
3175 }
3176 if (pos < max_search_start) {
3177 ReplacementStringBuilder::AddSubjectSlice(builder,
3178 pos + pattern_length,
3179 subject_length);
3180 }
3181 *match_pos = pos;
3182 return true;
3183}
3184
3185
3186static bool SearchStringMultiple(Handle<String> subject,
3187 Handle<String> pattern,
3188 Handle<JSArray> last_match_info,
3189 FixedArrayBuilder* builder) {
3190 ASSERT(subject->IsFlat());
3191 ASSERT(pattern->IsFlat());
3192 ASSERT(pattern->length() > 1);
3193
3194 // Treating as if a previous match was before first character.
3195 int match_pos = -pattern->length();
3196
3197 for (;;) { // Break when search complete.
3198 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3199 AssertNoAllocation no_gc;
3200 if (subject->IsAsciiRepresentation()) {
3201 Vector<const char> subject_vector = subject->ToAsciiVector();
3202 if (pattern->IsAsciiRepresentation()) {
3203 if (SearchStringMultiple(subject_vector,
3204 *pattern,
3205 pattern->ToAsciiVector(),
3206 builder,
3207 &match_pos)) break;
3208 } else {
3209 if (SearchStringMultiple(subject_vector,
3210 *pattern,
3211 pattern->ToUC16Vector(),
3212 builder,
3213 &match_pos)) break;
3214 }
3215 } else {
3216 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3217 if (pattern->IsAsciiRepresentation()) {
3218 if (SearchStringMultiple(subject_vector,
3219 *pattern,
3220 pattern->ToAsciiVector(),
3221 builder,
3222 &match_pos)) break;
3223 } else {
3224 if (SearchStringMultiple(subject_vector,
3225 *pattern,
3226 pattern->ToUC16Vector(),
3227 builder,
3228 &match_pos)) break;
3229 }
3230 }
3231 }
3232
3233 if (match_pos >= 0) {
3234 SetLastMatchInfoNoCaptures(subject,
3235 last_match_info,
3236 match_pos,
3237 match_pos + pattern->length());
3238 return true;
3239 }
3240 return false; // No matches at all.
3241}
3242
3243
3244static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3245 Handle<String> subject,
3246 Handle<JSRegExp> regexp,
3247 Handle<JSArray> last_match_array,
3248 FixedArrayBuilder* builder) {
3249 ASSERT(subject->IsFlat());
3250 int match_start = -1;
3251 int match_end = 0;
3252 int pos = 0;
3253 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3254 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3255
3256 OffsetsVector registers(required_registers);
3257 Vector<int> register_vector(registers.vector(), registers.length());
3258 int subject_length = subject->length();
3259
3260 for (;;) { // Break on failure, return on exception.
3261 RegExpImpl::IrregexpResult result =
3262 RegExpImpl::IrregexpExecOnce(regexp,
3263 subject,
3264 pos,
3265 register_vector);
3266 if (result == RegExpImpl::RE_SUCCESS) {
3267 match_start = register_vector[0];
3268 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3269 if (match_end < match_start) {
3270 ReplacementStringBuilder::AddSubjectSlice(builder,
3271 match_end,
3272 match_start);
3273 }
3274 match_end = register_vector[1];
3275 HandleScope loop_scope;
3276 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3277 if (match_start != match_end) {
3278 pos = match_end;
3279 } else {
3280 pos = match_end + 1;
3281 if (pos > subject_length) break;
3282 }
3283 } else if (result == RegExpImpl::RE_FAILURE) {
3284 break;
3285 } else {
3286 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3287 return result;
3288 }
3289 }
3290
3291 if (match_start >= 0) {
3292 if (match_end < subject_length) {
3293 ReplacementStringBuilder::AddSubjectSlice(builder,
3294 match_end,
3295 subject_length);
3296 }
3297 SetLastMatchInfoNoCaptures(subject,
3298 last_match_array,
3299 match_start,
3300 match_end);
3301 return RegExpImpl::RE_SUCCESS;
3302 } else {
3303 return RegExpImpl::RE_FAILURE; // No matches at all.
3304 }
3305}
3306
3307
3308static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3309 Handle<String> subject,
3310 Handle<JSRegExp> regexp,
3311 Handle<JSArray> last_match_array,
3312 FixedArrayBuilder* builder) {
3313
3314 ASSERT(subject->IsFlat());
3315 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3316 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3317
3318 OffsetsVector registers(required_registers);
3319 Vector<int> register_vector(registers.vector(), registers.length());
3320
3321 RegExpImpl::IrregexpResult result =
3322 RegExpImpl::IrregexpExecOnce(regexp,
3323 subject,
3324 0,
3325 register_vector);
3326
3327 int capture_count = regexp->CaptureCount();
3328 int subject_length = subject->length();
3329
3330 // Position to search from.
3331 int pos = 0;
3332 // End of previous match. Differs from pos if match was empty.
3333 int match_end = 0;
3334 if (result == RegExpImpl::RE_SUCCESS) {
3335 // Need to keep a copy of the previous match for creating last_match_info
3336 // at the end, so we have two vectors that we swap between.
3337 OffsetsVector registers2(required_registers);
3338 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3339
3340 do {
3341 int match_start = register_vector[0];
3342 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3343 if (match_end < match_start) {
3344 ReplacementStringBuilder::AddSubjectSlice(builder,
3345 match_end,
3346 match_start);
3347 }
3348 match_end = register_vector[1];
3349
3350 {
3351 // Avoid accumulating new handles inside loop.
3352 HandleScope temp_scope;
3353 // Arguments array to replace function is match, captures, index and
3354 // subject, i.e., 3 + capture count in total.
3355 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3356 elements->set(0, *Factory::NewSubString(subject,
3357 match_start,
3358 match_end));
3359 for (int i = 1; i <= capture_count; i++) {
3360 int start = register_vector[i * 2];
3361 if (start >= 0) {
3362 int end = register_vector[i * 2 + 1];
3363 ASSERT(start <= end);
3364 Handle<String> substring = Factory::NewSubString(subject,
3365 start,
3366 end);
3367 elements->set(i, *substring);
3368 } else {
3369 ASSERT(register_vector[i * 2 + 1] < 0);
3370 elements->set(i, Heap::undefined_value());
3371 }
3372 }
3373 elements->set(capture_count + 1, Smi::FromInt(match_start));
3374 elements->set(capture_count + 2, *subject);
3375 builder->Add(*Factory::NewJSArrayWithElements(elements));
3376 }
3377 // Swap register vectors, so the last successful match is in
3378 // prev_register_vector.
3379 Vector<int> tmp = prev_register_vector;
3380 prev_register_vector = register_vector;
3381 register_vector = tmp;
3382
3383 if (match_end > match_start) {
3384 pos = match_end;
3385 } else {
3386 pos = match_end + 1;
3387 if (pos > subject_length) {
3388 break;
3389 }
3390 }
3391
3392 result = RegExpImpl::IrregexpExecOnce(regexp,
3393 subject,
3394 pos,
3395 register_vector);
3396 } while (result == RegExpImpl::RE_SUCCESS);
3397
3398 if (result != RegExpImpl::RE_EXCEPTION) {
3399 // Finished matching, with at least one match.
3400 if (match_end < subject_length) {
3401 ReplacementStringBuilder::AddSubjectSlice(builder,
3402 match_end,
3403 subject_length);
3404 }
3405
3406 int last_match_capture_count = (capture_count + 1) * 2;
3407 int last_match_array_size =
3408 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3409 last_match_array->EnsureSize(last_match_array_size);
3410 AssertNoAllocation no_gc;
3411 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3412 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3413 RegExpImpl::SetLastSubject(elements, *subject);
3414 RegExpImpl::SetLastInput(elements, *subject);
3415 for (int i = 0; i < last_match_capture_count; i++) {
3416 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3417 }
3418 return RegExpImpl::RE_SUCCESS;
3419 }
3420 }
3421 // No matches at all, return failure or exception result directly.
3422 return result;
3423}
3424
3425
3426static Object* Runtime_RegExpExecMultiple(Arguments args) {
3427 ASSERT(args.length() == 4);
3428 HandleScope handles;
3429
3430 CONVERT_ARG_CHECKED(String, subject, 1);
3431 if (!subject->IsFlat()) { FlattenString(subject); }
3432 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3433 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3434 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3435
3436 ASSERT(last_match_info->HasFastElements());
3437 ASSERT(regexp->GetFlags().is_global());
3438 Handle<FixedArray> result_elements;
3439 if (result_array->HasFastElements()) {
3440 result_elements =
3441 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3442 } else {
3443 result_elements = Factory::NewFixedArrayWithHoles(16);
3444 }
3445 FixedArrayBuilder builder(result_elements);
3446
3447 if (regexp->TypeTag() == JSRegExp::ATOM) {
3448 Handle<String> pattern(
3449 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3450 int pattern_length = pattern->length();
3451 if (pattern_length == 1) {
3452 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3453 return *builder.ToJSArray(result_array);
3454 }
3455 return Heap::null_value();
3456 }
3457
3458 if (!pattern->IsFlat()) FlattenString(pattern);
3459 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3460 return *builder.ToJSArray(result_array);
3461 }
3462 return Heap::null_value();
3463 }
3464
3465 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3466
3467 RegExpImpl::IrregexpResult result;
3468 if (regexp->CaptureCount() == 0) {
3469 result = SearchRegExpNoCaptureMultiple(subject,
3470 regexp,
3471 last_match_info,
3472 &builder);
3473 } else {
3474 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3475 }
3476 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3477 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3478 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3479 return Failure::Exception();
3480}
3481
3482
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003483static Object* Runtime_NumberToRadixString(Arguments args) {
3484 NoHandleAllocation ha;
3485 ASSERT(args.length() == 2);
3486
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003487 // Fast case where the result is a one character string.
3488 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3489 int value = Smi::cast(args[0])->value();
3490 int radix = Smi::cast(args[1])->value();
3491 if (value >= 0 && value < radix) {
3492 RUNTIME_ASSERT(radix <= 36);
3493 // Character array used for conversion.
3494 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3495 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3496 }
3497 }
3498
3499 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003500 CONVERT_DOUBLE_CHECKED(value, args[0]);
3501 if (isnan(value)) {
3502 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3503 }
3504 if (isinf(value)) {
3505 if (value < 0) {
3506 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3507 }
3508 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3509 }
3510 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3511 int radix = FastD2I(radix_number);
3512 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3513 char* str = DoubleToRadixCString(value, radix);
3514 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3515 DeleteArray(str);
3516 return result;
3517}
3518
3519
3520static Object* Runtime_NumberToFixed(Arguments args) {
3521 NoHandleAllocation ha;
3522 ASSERT(args.length() == 2);
3523
3524 CONVERT_DOUBLE_CHECKED(value, args[0]);
3525 if (isnan(value)) {
3526 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3527 }
3528 if (isinf(value)) {
3529 if (value < 0) {
3530 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3531 }
3532 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3533 }
3534 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3535 int f = FastD2I(f_number);
3536 RUNTIME_ASSERT(f >= 0);
3537 char* str = DoubleToFixedCString(value, f);
3538 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3539 DeleteArray(str);
3540 return res;
3541}
3542
3543
3544static Object* Runtime_NumberToExponential(Arguments args) {
3545 NoHandleAllocation ha;
3546 ASSERT(args.length() == 2);
3547
3548 CONVERT_DOUBLE_CHECKED(value, args[0]);
3549 if (isnan(value)) {
3550 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3551 }
3552 if (isinf(value)) {
3553 if (value < 0) {
3554 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3555 }
3556 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3557 }
3558 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3559 int f = FastD2I(f_number);
3560 RUNTIME_ASSERT(f >= -1 && f <= 20);
3561 char* str = DoubleToExponentialCString(value, f);
3562 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3563 DeleteArray(str);
3564 return res;
3565}
3566
3567
3568static Object* Runtime_NumberToPrecision(Arguments args) {
3569 NoHandleAllocation ha;
3570 ASSERT(args.length() == 2);
3571
3572 CONVERT_DOUBLE_CHECKED(value, args[0]);
3573 if (isnan(value)) {
3574 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3575 }
3576 if (isinf(value)) {
3577 if (value < 0) {
3578 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3579 }
3580 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3581 }
3582 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3583 int f = FastD2I(f_number);
3584 RUNTIME_ASSERT(f >= 1 && f <= 21);
3585 char* str = DoubleToPrecisionCString(value, f);
3586 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3587 DeleteArray(str);
3588 return res;
3589}
3590
3591
3592// Returns a single character string where first character equals
3593// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003594static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003595 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003596 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003597 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003598 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003599 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003600 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003601}
3602
3603
3604Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3605 // Handle [] indexing on Strings
3606 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003607 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3608 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003609 }
3610
3611 // Handle [] indexing on String objects
3612 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003613 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3614 Handle<Object> result =
3615 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3616 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003617 }
3618
3619 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003620 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003621 return prototype->GetElement(index);
3622 }
3623
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003624 return GetElement(object, index);
3625}
3626
3627
3628Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003629 return object->GetElement(index);
3630}
3631
3632
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003633Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3634 HandleScope scope;
3635
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003636 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003637 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003638 Handle<Object> error =
3639 Factory::NewTypeError("non_object_property_load",
3640 HandleVector(args, 2));
3641 return Top::Throw(*error);
3642 }
3643
3644 // Check if the given key is an array index.
3645 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003646 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003647 return GetElementOrCharAt(object, index);
3648 }
3649
3650 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003651 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003652 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003653 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003654 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003655 bool has_pending_exception = false;
3656 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003657 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003658 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003659 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003660 }
3661
ager@chromium.org32912102009-01-16 10:38:43 +00003662 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003663 // the element if so.
3664 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003665 return GetElementOrCharAt(object, index);
3666 } else {
3667 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003668 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003669 }
3670}
3671
3672
3673static Object* Runtime_GetProperty(Arguments args) {
3674 NoHandleAllocation ha;
3675 ASSERT(args.length() == 2);
3676
3677 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003678 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003679
3680 return Runtime::GetObjectProperty(object, key);
3681}
3682
3683
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003684// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003685static Object* Runtime_KeyedGetProperty(Arguments args) {
3686 NoHandleAllocation ha;
3687 ASSERT(args.length() == 2);
3688
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003689 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003690 // itself.
3691 //
3692 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003693 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003694 // global proxy object never has properties. This is the case
3695 // because the global proxy object forwards everything to its hidden
3696 // prototype including local lookups.
3697 //
3698 // Additionally, we need to make sure that we do not cache results
3699 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003700 if (args[0]->IsJSObject() &&
3701 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003702 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003703 args[1]->IsString()) {
3704 JSObject* receiver = JSObject::cast(args[0]);
3705 String* key = String::cast(args[1]);
3706 if (receiver->HasFastProperties()) {
3707 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003708 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003709 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3710 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003711 Object* value = receiver->FastPropertyAt(offset);
3712 return value->IsTheHole() ? Heap::undefined_value() : value;
3713 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003714 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003715 LookupResult result;
3716 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003717 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003718 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003719 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003720 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003721 }
3722 } else {
3723 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003724 StringDictionary* dictionary = receiver->property_dictionary();
3725 int entry = dictionary->FindEntry(key);
3726 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003727 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003728 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003729 if (!receiver->IsGlobalObject()) return value;
3730 value = JSGlobalPropertyCell::cast(value)->value();
3731 if (!value->IsTheHole()) return value;
3732 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003733 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003734 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003735 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3736 // Fast case for string indexing using [] with a smi index.
3737 HandleScope scope;
3738 Handle<String> str = args.at<String>(0);
3739 int index = Smi::cast(args[1])->value();
3740 Handle<Object> result = GetCharAt(str, index);
3741 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003742 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003743
3744 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003745 return Runtime::GetObjectProperty(args.at<Object>(0),
3746 args.at<Object>(1));
3747}
3748
3749
ager@chromium.org5c838252010-02-19 08:53:10 +00003750static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3751 ASSERT(args.length() == 5);
3752 HandleScope scope;
3753 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3754 CONVERT_CHECKED(String, name, args[1]);
3755 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3756 CONVERT_CHECKED(JSFunction, fun, args[3]);
3757 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3758 int unchecked = flag_attr->value();
3759 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3760 RUNTIME_ASSERT(!obj->IsNull());
3761 LookupResult result;
3762 obj->LocalLookupRealNamedProperty(name, &result);
3763
3764 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3765 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3766 // delete it to avoid running into trouble in DefineAccessor, which
3767 // handles this incorrectly if the property is readonly (does nothing)
3768 if (result.IsProperty() &&
3769 (result.type() == FIELD || result.type() == NORMAL
3770 || result.type() == CONSTANT_FUNCTION)) {
3771 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3772 }
3773 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3774}
3775
3776static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3777 ASSERT(args.length() == 4);
3778 HandleScope scope;
3779 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3780 CONVERT_ARG_CHECKED(String, name, 1);
3781 Handle<Object> obj_value = args.at<Object>(2);
3782
3783 CONVERT_CHECKED(Smi, flag, args[3]);
3784 int unchecked = flag->value();
3785 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3786
3787 LookupResult result;
3788 js_object->LocalLookupRealNamedProperty(*name, &result);
3789
3790 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3791
3792 // Take special care when attributes are different and there is already
3793 // a property. For simplicity we normalize the property which enables us
3794 // to not worry about changing the instance_descriptor and creating a new
3795 // map. The current version of SetObjectProperty does not handle attributes
3796 // correctly in the case where a property is a field and is reset with
3797 // new attributes.
3798 if (result.IsProperty() && attr != result.GetAttributes()) {
3799 // New attributes - normalize to avoid writing to instance descriptor
3800 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3801 // Use IgnoreAttributes version since a readonly property may be
3802 // overridden and SetProperty does not allow this.
3803 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3804 *obj_value,
3805 attr);
3806 }
3807 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3808}
3809
3810
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003811Object* Runtime::SetObjectProperty(Handle<Object> object,
3812 Handle<Object> key,
3813 Handle<Object> value,
3814 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003815 HandleScope scope;
3816
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003817 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003818 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003819 Handle<Object> error =
3820 Factory::NewTypeError("non_object_property_store",
3821 HandleVector(args, 2));
3822 return Top::Throw(*error);
3823 }
3824
3825 // If the object isn't a JavaScript object, we ignore the store.
3826 if (!object->IsJSObject()) return *value;
3827
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003828 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003830 // Check if the given key is an array index.
3831 uint32_t index;
3832 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003833 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3834 // of a string using [] notation. We need to support this too in
3835 // JavaScript.
3836 // In the case of a String object we just need to redirect the assignment to
3837 // the underlying string if the index is in range. Since the underlying
3838 // string does nothing with the assignment then we can ignore such
3839 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003840 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003841 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003842 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003843
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003844 Handle<Object> result = SetElement(js_object, index, value);
3845 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003846 return *value;
3847 }
3848
3849 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003850 Handle<Object> result;
3851 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003852 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003853 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003854 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003855 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003856 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003857 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003858 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003859 return *value;
3860 }
3861
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003862 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003863 bool has_pending_exception = false;
3864 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3865 if (has_pending_exception) return Failure::Exception();
3866 Handle<String> name = Handle<String>::cast(converted);
3867
3868 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003869 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003870 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003871 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003872 }
3873}
3874
3875
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003876Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3877 Handle<Object> key,
3878 Handle<Object> value,
3879 PropertyAttributes attr) {
3880 HandleScope scope;
3881
3882 // Check if the given key is an array index.
3883 uint32_t index;
3884 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003885 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3886 // of a string using [] notation. We need to support this too in
3887 // JavaScript.
3888 // In the case of a String object we just need to redirect the assignment to
3889 // the underlying string if the index is in range. Since the underlying
3890 // string does nothing with the assignment then we can ignore such
3891 // assignments.
3892 if (js_object->IsStringObjectWithCharacterAt(index)) {
3893 return *value;
3894 }
3895
3896 return js_object->SetElement(index, *value);
3897 }
3898
3899 if (key->IsString()) {
3900 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003901 return js_object->SetElement(index, *value);
3902 } else {
3903 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003904 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003905 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3906 *value,
3907 attr);
3908 }
3909 }
3910
3911 // Call-back into JavaScript to convert the key to a string.
3912 bool has_pending_exception = false;
3913 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3914 if (has_pending_exception) return Failure::Exception();
3915 Handle<String> name = Handle<String>::cast(converted);
3916
3917 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003918 return js_object->SetElement(index, *value);
3919 } else {
3920 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3921 }
3922}
3923
3924
ager@chromium.orge2902be2009-06-08 12:21:35 +00003925Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3926 Handle<Object> key) {
3927 HandleScope scope;
3928
3929 // Check if the given key is an array index.
3930 uint32_t index;
3931 if (Array::IndexFromObject(*key, &index)) {
3932 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3933 // characters of a string using [] notation. In the case of a
3934 // String object we just need to redirect the deletion to the
3935 // underlying string if the index is in range. Since the
3936 // underlying string does nothing with the deletion, we can ignore
3937 // such deletions.
3938 if (js_object->IsStringObjectWithCharacterAt(index)) {
3939 return Heap::true_value();
3940 }
3941
3942 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3943 }
3944
3945 Handle<String> key_string;
3946 if (key->IsString()) {
3947 key_string = Handle<String>::cast(key);
3948 } else {
3949 // Call-back into JavaScript to convert the key to a string.
3950 bool has_pending_exception = false;
3951 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3952 if (has_pending_exception) return Failure::Exception();
3953 key_string = Handle<String>::cast(converted);
3954 }
3955
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003956 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003957 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3958}
3959
3960
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003961static Object* Runtime_SetProperty(Arguments args) {
3962 NoHandleAllocation ha;
3963 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3964
3965 Handle<Object> object = args.at<Object>(0);
3966 Handle<Object> key = args.at<Object>(1);
3967 Handle<Object> value = args.at<Object>(2);
3968
3969 // Compute attributes.
3970 PropertyAttributes attributes = NONE;
3971 if (args.length() == 4) {
3972 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003973 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003974 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003975 RUNTIME_ASSERT(
3976 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3977 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003978 }
3979 return Runtime::SetObjectProperty(object, key, value, attributes);
3980}
3981
3982
3983// Set a local property, even if it is READ_ONLY. If the property does not
3984// exist, it will be added with attributes NONE.
3985static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
3986 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003987 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003988 CONVERT_CHECKED(JSObject, object, args[0]);
3989 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003990 // Compute attributes.
3991 PropertyAttributes attributes = NONE;
3992 if (args.length() == 4) {
3993 CONVERT_CHECKED(Smi, value_obj, args[3]);
3994 int unchecked_value = value_obj->value();
3995 // Only attribute bits should be set.
3996 RUNTIME_ASSERT(
3997 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3998 attributes = static_cast<PropertyAttributes>(unchecked_value);
3999 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004000
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004001 return object->
4002 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004003}
4004
4005
4006static Object* Runtime_DeleteProperty(Arguments args) {
4007 NoHandleAllocation ha;
4008 ASSERT(args.length() == 2);
4009
4010 CONVERT_CHECKED(JSObject, object, args[0]);
4011 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004012 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004013}
4014
4015
ager@chromium.org9085a012009-05-11 19:22:57 +00004016static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4017 Handle<String> key) {
4018 if (object->HasLocalProperty(*key)) return Heap::true_value();
4019 // Handle hidden prototypes. If there's a hidden prototype above this thing
4020 // then we have to check it for properties, because they are supposed to
4021 // look like they are on this object.
4022 Handle<Object> proto(object->GetPrototype());
4023 if (proto->IsJSObject() &&
4024 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4025 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4026 }
4027 return Heap::false_value();
4028}
4029
4030
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031static Object* Runtime_HasLocalProperty(Arguments args) {
4032 NoHandleAllocation ha;
4033 ASSERT(args.length() == 2);
4034 CONVERT_CHECKED(String, key, args[1]);
4035
ager@chromium.org9085a012009-05-11 19:22:57 +00004036 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004037 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004038 if (obj->IsJSObject()) {
4039 JSObject* object = JSObject::cast(obj);
4040 // Fast case - no interceptors.
4041 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4042 // Slow case. Either it's not there or we have an interceptor. We should
4043 // have handles for this kind of deal.
4044 HandleScope scope;
4045 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4046 Handle<String>(key));
4047 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004048 // Well, there is one exception: Handle [] on strings.
4049 uint32_t index;
4050 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004051 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004052 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004053 return Heap::true_value();
4054 }
4055 }
4056 return Heap::false_value();
4057}
4058
4059
4060static Object* Runtime_HasProperty(Arguments args) {
4061 NoHandleAllocation na;
4062 ASSERT(args.length() == 2);
4063
4064 // Only JS objects can have properties.
4065 if (args[0]->IsJSObject()) {
4066 JSObject* object = JSObject::cast(args[0]);
4067 CONVERT_CHECKED(String, key, args[1]);
4068 if (object->HasProperty(key)) return Heap::true_value();
4069 }
4070 return Heap::false_value();
4071}
4072
4073
4074static Object* Runtime_HasElement(Arguments args) {
4075 NoHandleAllocation na;
4076 ASSERT(args.length() == 2);
4077
4078 // Only JS objects can have elements.
4079 if (args[0]->IsJSObject()) {
4080 JSObject* object = JSObject::cast(args[0]);
4081 CONVERT_CHECKED(Smi, index_obj, args[1]);
4082 uint32_t index = index_obj->value();
4083 if (object->HasElement(index)) return Heap::true_value();
4084 }
4085 return Heap::false_value();
4086}
4087
4088
4089static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4090 NoHandleAllocation ha;
4091 ASSERT(args.length() == 2);
4092
4093 CONVERT_CHECKED(JSObject, object, args[0]);
4094 CONVERT_CHECKED(String, key, args[1]);
4095
4096 uint32_t index;
4097 if (key->AsArrayIndex(&index)) {
4098 return Heap::ToBoolean(object->HasElement(index));
4099 }
4100
ager@chromium.org870a0b62008-11-04 11:43:05 +00004101 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4102 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004103}
4104
4105
4106static Object* Runtime_GetPropertyNames(Arguments args) {
4107 HandleScope scope;
4108 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004109 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004110 return *GetKeysFor(object);
4111}
4112
4113
4114// Returns either a FixedArray as Runtime_GetPropertyNames,
4115// or, if the given object has an enum cache that contains
4116// all enumerable properties of the object and its prototypes
4117// have none, the map of the object. This is used to speed up
4118// the check for deletions during a for-in.
4119static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4120 ASSERT(args.length() == 1);
4121
4122 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4123
4124 if (raw_object->IsSimpleEnum()) return raw_object->map();
4125
4126 HandleScope scope;
4127 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004128 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4129 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004130
4131 // Test again, since cache may have been built by preceding call.
4132 if (object->IsSimpleEnum()) return object->map();
4133
4134 return *content;
4135}
4136
4137
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004138// Find the length of the prototype chain that is to to handled as one. If a
4139// prototype object is hidden it is to be viewed as part of the the object it
4140// is prototype for.
4141static int LocalPrototypeChainLength(JSObject* obj) {
4142 int count = 1;
4143 Object* proto = obj->GetPrototype();
4144 while (proto->IsJSObject() &&
4145 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4146 count++;
4147 proto = JSObject::cast(proto)->GetPrototype();
4148 }
4149 return count;
4150}
4151
4152
4153// Return the names of the local named properties.
4154// args[0]: object
4155static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4156 HandleScope scope;
4157 ASSERT(args.length() == 1);
4158 if (!args[0]->IsJSObject()) {
4159 return Heap::undefined_value();
4160 }
4161 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4162
4163 // Skip the global proxy as it has no properties and always delegates to the
4164 // real global object.
4165 if (obj->IsJSGlobalProxy()) {
4166 // Only collect names if access is permitted.
4167 if (obj->IsAccessCheckNeeded() &&
4168 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4169 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4170 return *Factory::NewJSArray(0);
4171 }
4172 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4173 }
4174
4175 // Find the number of objects making up this.
4176 int length = LocalPrototypeChainLength(*obj);
4177
4178 // Find the number of local properties for each of the objects.
4179 int* local_property_count = NewArray<int>(length);
4180 int total_property_count = 0;
4181 Handle<JSObject> jsproto = obj;
4182 for (int i = 0; i < length; i++) {
4183 // Only collect names if access is permitted.
4184 if (jsproto->IsAccessCheckNeeded() &&
4185 !Top::MayNamedAccess(*jsproto,
4186 Heap::undefined_value(),
4187 v8::ACCESS_KEYS)) {
4188 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4189 return *Factory::NewJSArray(0);
4190 }
4191 int n;
4192 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4193 local_property_count[i] = n;
4194 total_property_count += n;
4195 if (i < length - 1) {
4196 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4197 }
4198 }
4199
4200 // Allocate an array with storage for all the property names.
4201 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4202
4203 // Get the property names.
4204 jsproto = obj;
4205 int proto_with_hidden_properties = 0;
4206 for (int i = 0; i < length; i++) {
4207 jsproto->GetLocalPropertyNames(*names,
4208 i == 0 ? 0 : local_property_count[i - 1]);
4209 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4210 proto_with_hidden_properties++;
4211 }
4212 if (i < length - 1) {
4213 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4214 }
4215 }
4216
4217 // Filter out name of hidden propeties object.
4218 if (proto_with_hidden_properties > 0) {
4219 Handle<FixedArray> old_names = names;
4220 names = Factory::NewFixedArray(
4221 names->length() - proto_with_hidden_properties);
4222 int dest_pos = 0;
4223 for (int i = 0; i < total_property_count; i++) {
4224 Object* name = old_names->get(i);
4225 if (name == Heap::hidden_symbol()) {
4226 continue;
4227 }
4228 names->set(dest_pos++, name);
4229 }
4230 }
4231
4232 DeleteArray(local_property_count);
4233 return *Factory::NewJSArrayWithElements(names);
4234}
4235
4236
4237// Return the names of the local indexed properties.
4238// args[0]: object
4239static Object* Runtime_GetLocalElementNames(Arguments args) {
4240 HandleScope scope;
4241 ASSERT(args.length() == 1);
4242 if (!args[0]->IsJSObject()) {
4243 return Heap::undefined_value();
4244 }
4245 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4246
4247 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4248 Handle<FixedArray> names = Factory::NewFixedArray(n);
4249 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4250 return *Factory::NewJSArrayWithElements(names);
4251}
4252
4253
4254// Return information on whether an object has a named or indexed interceptor.
4255// args[0]: object
4256static Object* Runtime_GetInterceptorInfo(Arguments args) {
4257 HandleScope scope;
4258 ASSERT(args.length() == 1);
4259 if (!args[0]->IsJSObject()) {
4260 return Smi::FromInt(0);
4261 }
4262 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4263
4264 int result = 0;
4265 if (obj->HasNamedInterceptor()) result |= 2;
4266 if (obj->HasIndexedInterceptor()) result |= 1;
4267
4268 return Smi::FromInt(result);
4269}
4270
4271
4272// Return property names from named interceptor.
4273// args[0]: object
4274static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4275 HandleScope scope;
4276 ASSERT(args.length() == 1);
4277 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4278
4279 if (obj->HasNamedInterceptor()) {
4280 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4281 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4282 }
4283 return Heap::undefined_value();
4284}
4285
4286
4287// Return element names from indexed interceptor.
4288// args[0]: object
4289static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4290 HandleScope scope;
4291 ASSERT(args.length() == 1);
4292 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4293
4294 if (obj->HasIndexedInterceptor()) {
4295 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4296 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4297 }
4298 return Heap::undefined_value();
4299}
4300
4301
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004302static Object* Runtime_LocalKeys(Arguments args) {
4303 ASSERT_EQ(args.length(), 1);
4304 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4305 HandleScope scope;
4306 Handle<JSObject> object(raw_object);
4307 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4308 LOCAL_ONLY);
4309 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4310 // property array and since the result is mutable we have to create
4311 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004312 int length = contents->length();
4313 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4314 for (int i = 0; i < length; i++) {
4315 Object* entry = contents->get(i);
4316 if (entry->IsString()) {
4317 copy->set(i, entry);
4318 } else {
4319 ASSERT(entry->IsNumber());
4320 HandleScope scope;
4321 Handle<Object> entry_handle(entry);
4322 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4323 copy->set(i, *entry_str);
4324 }
4325 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004326 return *Factory::NewJSArrayWithElements(copy);
4327}
4328
4329
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004330static Object* Runtime_GetArgumentsProperty(Arguments args) {
4331 NoHandleAllocation ha;
4332 ASSERT(args.length() == 1);
4333
4334 // Compute the frame holding the arguments.
4335 JavaScriptFrameIterator it;
4336 it.AdvanceToArgumentsFrame();
4337 JavaScriptFrame* frame = it.frame();
4338
4339 // Get the actual number of provided arguments.
4340 const uint32_t n = frame->GetProvidedParametersCount();
4341
4342 // Try to convert the key to an index. If successful and within
4343 // index return the the argument from the frame.
4344 uint32_t index;
4345 if (Array::IndexFromObject(args[0], &index) && index < n) {
4346 return frame->GetParameter(index);
4347 }
4348
4349 // Convert the key to a string.
4350 HandleScope scope;
4351 bool exception = false;
4352 Handle<Object> converted =
4353 Execution::ToString(args.at<Object>(0), &exception);
4354 if (exception) return Failure::Exception();
4355 Handle<String> key = Handle<String>::cast(converted);
4356
4357 // Try to convert the string key into an array index.
4358 if (key->AsArrayIndex(&index)) {
4359 if (index < n) {
4360 return frame->GetParameter(index);
4361 } else {
4362 return Top::initial_object_prototype()->GetElement(index);
4363 }
4364 }
4365
4366 // Handle special arguments properties.
4367 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4368 if (key->Equals(Heap::callee_symbol())) return frame->function();
4369
4370 // Lookup in the initial Object.prototype object.
4371 return Top::initial_object_prototype()->GetProperty(*key);
4372}
4373
4374
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004375static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004376 HandleScope scope;
4377
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004378 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004379 Handle<Object> object = args.at<Object>(0);
4380 if (object->IsJSObject()) {
4381 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004382 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4383 js_object->TransformToFastProperties(0);
4384 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004385 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004386 return *object;
4387}
4388
4389
4390static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004391 HandleScope scope;
4392
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004393 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004394 Handle<Object> object = args.at<Object>(0);
4395 if (object->IsJSObject()) {
4396 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004397 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004398 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004399 return *object;
4400}
4401
4402
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004403static Object* Runtime_ToBool(Arguments args) {
4404 NoHandleAllocation ha;
4405 ASSERT(args.length() == 1);
4406
4407 return args[0]->ToBoolean();
4408}
4409
4410
4411// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4412// Possible optimizations: put the type string into the oddballs.
4413static Object* Runtime_Typeof(Arguments args) {
4414 NoHandleAllocation ha;
4415
4416 Object* obj = args[0];
4417 if (obj->IsNumber()) return Heap::number_symbol();
4418 HeapObject* heap_obj = HeapObject::cast(obj);
4419
4420 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004421 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004422
4423 InstanceType instance_type = heap_obj->map()->instance_type();
4424 if (instance_type < FIRST_NONSTRING_TYPE) {
4425 return Heap::string_symbol();
4426 }
4427
4428 switch (instance_type) {
4429 case ODDBALL_TYPE:
4430 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4431 return Heap::boolean_symbol();
4432 }
4433 if (heap_obj->IsNull()) {
4434 return Heap::object_symbol();
4435 }
4436 ASSERT(heap_obj->IsUndefined());
4437 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004438 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439 return Heap::function_symbol();
4440 default:
4441 // For any kind of object not handled above, the spec rule for
4442 // host objects gives that it is okay to return "object"
4443 return Heap::object_symbol();
4444 }
4445}
4446
4447
lrn@chromium.org25156de2010-04-06 13:10:27 +00004448static bool AreDigits(const char*s, int from, int to) {
4449 for (int i = from; i < to; i++) {
4450 if (s[i] < '0' || s[i] > '9') return false;
4451 }
4452
4453 return true;
4454}
4455
4456
4457static int ParseDecimalInteger(const char*s, int from, int to) {
4458 ASSERT(to - from < 10); // Overflow is not possible.
4459 ASSERT(from < to);
4460 int d = s[from] - '0';
4461
4462 for (int i = from + 1; i < to; i++) {
4463 d = 10 * d + (s[i] - '0');
4464 }
4465
4466 return d;
4467}
4468
4469
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004470static Object* Runtime_StringToNumber(Arguments args) {
4471 NoHandleAllocation ha;
4472 ASSERT(args.length() == 1);
4473 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004474 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004475
4476 // Fast case: short integer or some sorts of junk values.
4477 int len = subject->length();
4478 if (subject->IsSeqAsciiString()) {
4479 if (len == 0) return Smi::FromInt(0);
4480
4481 char const* data = SeqAsciiString::cast(subject)->GetChars();
4482 bool minus = (data[0] == '-');
4483 int start_pos = (minus ? 1 : 0);
4484
4485 if (start_pos == len) {
4486 return Heap::nan_value();
4487 } else if (data[start_pos] > '9') {
4488 // Fast check for a junk value. A valid string may start from a
4489 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4490 // the 'I' character ('Infinity'). All of that have codes not greater than
4491 // '9' except 'I'.
4492 if (data[start_pos] != 'I') {
4493 return Heap::nan_value();
4494 }
4495 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4496 // The maximal/minimal smi has 10 digits. If the string has less digits we
4497 // know it will fit into the smi-data type.
4498 int d = ParseDecimalInteger(data, start_pos, len);
4499 if (minus) {
4500 if (d == 0) return Heap::minus_zero_value();
4501 d = -d;
4502 }
4503 return Smi::FromInt(d);
4504 }
4505 }
4506
4507 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004508 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4509}
4510
4511
4512static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4513 NoHandleAllocation ha;
4514 ASSERT(args.length() == 1);
4515
4516 CONVERT_CHECKED(JSArray, codes, args[0]);
4517 int length = Smi::cast(codes->length())->value();
4518
4519 // Check if the string can be ASCII.
4520 int i;
4521 for (i = 0; i < length; i++) {
4522 Object* element = codes->GetElement(i);
4523 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4524 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4525 break;
4526 }
4527
4528 Object* object = NULL;
4529 if (i == length) { // The string is ASCII.
4530 object = Heap::AllocateRawAsciiString(length);
4531 } else { // The string is not ASCII.
4532 object = Heap::AllocateRawTwoByteString(length);
4533 }
4534
4535 if (object->IsFailure()) return object;
4536 String* result = String::cast(object);
4537 for (int i = 0; i < length; i++) {
4538 Object* element = codes->GetElement(i);
4539 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004540 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004541 }
4542 return result;
4543}
4544
4545
4546// kNotEscaped is generated by the following:
4547//
4548// #!/bin/perl
4549// for (my $i = 0; $i < 256; $i++) {
4550// print "\n" if $i % 16 == 0;
4551// my $c = chr($i);
4552// my $escaped = 1;
4553// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4554// print $escaped ? "0, " : "1, ";
4555// }
4556
4557
4558static bool IsNotEscaped(uint16_t character) {
4559 // Only for 8 bit characters, the rest are always escaped (in a different way)
4560 ASSERT(character < 256);
4561 static const char kNotEscaped[256] = {
4562 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4563 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4564 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4565 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4566 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4567 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4568 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4569 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4570 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4572 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4573 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4574 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4575 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4577 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4578 };
4579 return kNotEscaped[character] != 0;
4580}
4581
4582
4583static Object* Runtime_URIEscape(Arguments args) {
4584 const char hex_chars[] = "0123456789ABCDEF";
4585 NoHandleAllocation ha;
4586 ASSERT(args.length() == 1);
4587 CONVERT_CHECKED(String, source, args[0]);
4588
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004589 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004590
4591 int escaped_length = 0;
4592 int length = source->length();
4593 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004594 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004595 buffer->Reset(source);
4596 while (buffer->has_more()) {
4597 uint16_t character = buffer->GetNext();
4598 if (character >= 256) {
4599 escaped_length += 6;
4600 } else if (IsNotEscaped(character)) {
4601 escaped_length++;
4602 } else {
4603 escaped_length += 3;
4604 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004605 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004606 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004607 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004608 Top::context()->mark_out_of_memory();
4609 return Failure::OutOfMemoryException();
4610 }
4611 }
4612 }
4613 // No length change implies no change. Return original string if no change.
4614 if (escaped_length == length) {
4615 return source;
4616 }
4617 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4618 if (o->IsFailure()) return o;
4619 String* destination = String::cast(o);
4620 int dest_position = 0;
4621
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004622 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004623 buffer->Rewind();
4624 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004625 uint16_t chr = buffer->GetNext();
4626 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004627 destination->Set(dest_position, '%');
4628 destination->Set(dest_position+1, 'u');
4629 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4630 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4631 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4632 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004633 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004634 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004635 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004636 dest_position++;
4637 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004638 destination->Set(dest_position, '%');
4639 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4640 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004641 dest_position += 3;
4642 }
4643 }
4644 return destination;
4645}
4646
4647
4648static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4649 static const signed char kHexValue['g'] = {
4650 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4651 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4652 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4653 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4654 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4655 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4656 -1, 10, 11, 12, 13, 14, 15 };
4657
4658 if (character1 > 'f') return -1;
4659 int hi = kHexValue[character1];
4660 if (hi == -1) return -1;
4661 if (character2 > 'f') return -1;
4662 int lo = kHexValue[character2];
4663 if (lo == -1) return -1;
4664 return (hi << 4) + lo;
4665}
4666
4667
ager@chromium.org870a0b62008-11-04 11:43:05 +00004668static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004669 int i,
4670 int length,
4671 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004672 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004673 int32_t hi = 0;
4674 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004675 if (character == '%' &&
4676 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004677 source->Get(i + 1) == 'u' &&
4678 (hi = TwoDigitHex(source->Get(i + 2),
4679 source->Get(i + 3))) != -1 &&
4680 (lo = TwoDigitHex(source->Get(i + 4),
4681 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004682 *step = 6;
4683 return (hi << 8) + lo;
4684 } else if (character == '%' &&
4685 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004686 (lo = TwoDigitHex(source->Get(i + 1),
4687 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004688 *step = 3;
4689 return lo;
4690 } else {
4691 *step = 1;
4692 return character;
4693 }
4694}
4695
4696
4697static Object* Runtime_URIUnescape(Arguments args) {
4698 NoHandleAllocation ha;
4699 ASSERT(args.length() == 1);
4700 CONVERT_CHECKED(String, source, args[0]);
4701
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004702 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004703
4704 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004705 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004706
4707 int unescaped_length = 0;
4708 for (int i = 0; i < length; unescaped_length++) {
4709 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004710 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004711 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004712 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004713 i += step;
4714 }
4715
4716 // No length change implies no change. Return original string if no change.
4717 if (unescaped_length == length)
4718 return source;
4719
4720 Object* o = ascii ?
4721 Heap::AllocateRawAsciiString(unescaped_length) :
4722 Heap::AllocateRawTwoByteString(unescaped_length);
4723 if (o->IsFailure()) return o;
4724 String* destination = String::cast(o);
4725
4726 int dest_position = 0;
4727 for (int i = 0; i < length; dest_position++) {
4728 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004729 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004730 i += step;
4731 }
4732 return destination;
4733}
4734
4735
4736static Object* Runtime_StringParseInt(Arguments args) {
4737 NoHandleAllocation ha;
4738
4739 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004740 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004741
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004742 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004743
lrn@chromium.org25156de2010-04-06 13:10:27 +00004744 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4745 double value = StringToInt(s, radix);
4746 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004747 return Heap::nan_value();
4748}
4749
4750
4751static Object* Runtime_StringParseFloat(Arguments args) {
4752 NoHandleAllocation ha;
4753 CONVERT_CHECKED(String, str, args[0]);
4754
4755 // ECMA-262 section 15.1.2.3, empty string is NaN
4756 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4757
4758 // Create a number object from the value.
4759 return Heap::NumberFromDouble(value);
4760}
4761
4762
4763static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4764static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4765
4766
4767template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004768static Object* ConvertCaseHelper(String* s,
4769 int length,
4770 int input_string_length,
4771 unibrow::Mapping<Converter, 128>* mapping) {
4772 // We try this twice, once with the assumption that the result is no longer
4773 // than the input and, if that assumption breaks, again with the exact
4774 // length. This may not be pretty, but it is nicer than what was here before
4775 // and I hereby claim my vaffel-is.
4776 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777 // Allocate the resulting string.
4778 //
4779 // NOTE: This assumes that the upper/lower case of an ascii
4780 // character is also ascii. This is currently the case, but it
4781 // might break in the future if we implement more context and locale
4782 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004783 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784 ? Heap::AllocateRawAsciiString(length)
4785 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004786 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004787 String* result = String::cast(o);
4788 bool has_changed_character = false;
4789
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004790 // Convert all characters to upper case, assuming that they will fit
4791 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004792 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004794 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004795 // We can assume that the string is not empty
4796 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004797 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004798 bool has_next = buffer->has_more();
4799 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004800 int char_length = mapping->get(current, next, chars);
4801 if (char_length == 0) {
4802 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004803 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004804 i++;
4805 } else if (char_length == 1) {
4806 // Common case: converting the letter resulted in one character.
4807 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004808 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004809 has_changed_character = true;
4810 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004811 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004812 // We've assumed that the result would be as long as the
4813 // input but here is a character that converts to several
4814 // characters. No matter, we calculate the exact length
4815 // of the result and try the whole thing again.
4816 //
4817 // Note that this leaves room for optimization. We could just
4818 // memcpy what we already have to the result string. Also,
4819 // the result string is the last object allocated we could
4820 // "realloc" it and probably, in the vast majority of cases,
4821 // extend the existing string to be able to hold the full
4822 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004823 int next_length = 0;
4824 if (has_next) {
4825 next_length = mapping->get(next, 0, chars);
4826 if (next_length == 0) next_length = 1;
4827 }
4828 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829 while (buffer->has_more()) {
4830 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004831 // NOTE: we use 0 as the next character here because, while
4832 // the next character may affect what a character converts to,
4833 // it does not in any case affect the length of what it convert
4834 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004835 int char_length = mapping->get(current, 0, chars);
4836 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004837 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004838 if (current_length > Smi::kMaxValue) {
4839 Top::context()->mark_out_of_memory();
4840 return Failure::OutOfMemoryException();
4841 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004842 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004843 // Try again with the real length.
4844 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004845 } else {
4846 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004847 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004848 i++;
4849 }
4850 has_changed_character = true;
4851 }
4852 current = next;
4853 }
4854 if (has_changed_character) {
4855 return result;
4856 } else {
4857 // If we didn't actually change anything in doing the conversion
4858 // we simple return the result and let the converted string
4859 // become garbage; there is no reason to keep two identical strings
4860 // alive.
4861 return s;
4862 }
4863}
4864
4865
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004866static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4867 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4868 if (s->IsConsString()) {
4869 ASSERT(ConsString::cast(s)->second()->length() == 0);
4870 return SeqAsciiString::cast(ConsString::cast(s)->first());
4871 }
4872 return SeqAsciiString::cast(s);
4873}
4874
4875
4876namespace {
4877
4878struct ToLowerTraits {
4879 typedef unibrow::ToLowercase UnibrowConverter;
4880
4881 static bool ConvertAscii(char* dst, char* src, int length) {
4882 bool changed = false;
4883 for (int i = 0; i < length; ++i) {
4884 char c = src[i];
4885 if ('A' <= c && c <= 'Z') {
4886 c += ('a' - 'A');
4887 changed = true;
4888 }
4889 dst[i] = c;
4890 }
4891 return changed;
4892 }
4893};
4894
4895
4896struct ToUpperTraits {
4897 typedef unibrow::ToUppercase UnibrowConverter;
4898
4899 static bool ConvertAscii(char* dst, char* src, int length) {
4900 bool changed = false;
4901 for (int i = 0; i < length; ++i) {
4902 char c = src[i];
4903 if ('a' <= c && c <= 'z') {
4904 c -= ('a' - 'A');
4905 changed = true;
4906 }
4907 dst[i] = c;
4908 }
4909 return changed;
4910 }
4911};
4912
4913} // namespace
4914
4915
4916template <typename ConvertTraits>
4917static Object* ConvertCase(
4918 Arguments args,
4919 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004920 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004921 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004922 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004923
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004924 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004925 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004926 if (length == 0) return s;
4927
4928 // Simpler handling of ascii strings.
4929 //
4930 // NOTE: This assumes that the upper/lower case of an ascii
4931 // character is also ascii. This is currently the case, but it
4932 // might break in the future if we implement more context and locale
4933 // dependent upper/lower conversions.
4934 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4935 if (seq_ascii != NULL) {
4936 Object* o = Heap::AllocateRawAsciiString(length);
4937 if (o->IsFailure()) return o;
4938 SeqAsciiString* result = SeqAsciiString::cast(o);
4939 bool has_changed_character = ConvertTraits::ConvertAscii(
4940 result->GetChars(), seq_ascii->GetChars(), length);
4941 return has_changed_character ? result : s;
4942 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004943
4944 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4945 if (answer->IsSmi()) {
4946 // Retry with correct length.
4947 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4948 }
4949 return answer; // This may be a failure.
4950}
4951
4952
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004953static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004954 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004955}
4956
4957
4958static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004959 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004960}
4961
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004962
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004963static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
4964 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
4965}
4966
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004967
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004968static Object* Runtime_StringTrim(Arguments args) {
4969 NoHandleAllocation ha;
4970 ASSERT(args.length() == 3);
4971
4972 CONVERT_CHECKED(String, s, args[0]);
4973 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
4974 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
4975
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004976 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004977 int length = s->length();
4978
4979 int left = 0;
4980 if (trimLeft) {
4981 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
4982 left++;
4983 }
4984 }
4985
4986 int right = length;
4987 if (trimRight) {
4988 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
4989 right--;
4990 }
4991 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004992 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004993}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004994
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004995
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004996template <typename schar, typename pchar>
4997void FindStringIndices(Vector<const schar> subject,
4998 Vector<const pchar> pattern,
4999 ZoneList<int>* indices,
5000 unsigned int limit) {
5001 ASSERT(limit > 0);
5002 // Collect indices of pattern in subject, and the end-of-string index.
5003 // Stop after finding at most limit values.
5004 StringSearchStrategy strategy =
5005 InitializeStringSearch(pattern, sizeof(schar) == 1);
5006 switch (strategy) {
5007 case SEARCH_FAIL: return;
5008 case SEARCH_SHORT: {
5009 int pattern_length = pattern.length();
5010 int index = 0;
5011 while (limit > 0) {
5012 index = SimpleIndexOf(subject, pattern, index);
5013 if (index < 0) return;
5014 indices->Add(index);
5015 index += pattern_length;
5016 limit--;
5017 }
5018 return;
5019 }
5020 case SEARCH_LONG: {
5021 int pattern_length = pattern.length();
5022 int index = 0;
5023 while (limit > 0) {
5024 index = ComplexIndexOf(subject, pattern, index);
5025 if (index < 0) return;
5026 indices->Add(index);
5027 index += pattern_length;
5028 limit--;
5029 }
5030 return;
5031 }
5032 default:
5033 UNREACHABLE();
5034 return;
5035 }
5036}
5037
5038template <typename schar>
5039inline void FindCharIndices(Vector<const schar> subject,
5040 const schar pattern_char,
5041 ZoneList<int>* indices,
5042 unsigned int limit) {
5043 // Collect indices of pattern_char in subject, and the end-of-string index.
5044 // Stop after finding at most limit values.
5045 int index = 0;
5046 while (limit > 0) {
5047 index = SingleCharIndexOf(subject, pattern_char, index);
5048 if (index < 0) return;
5049 indices->Add(index);
5050 index++;
5051 limit--;
5052 }
5053}
5054
5055
5056static Object* Runtime_StringSplit(Arguments args) {
5057 ASSERT(args.length() == 3);
5058 HandleScope handle_scope;
5059 CONVERT_ARG_CHECKED(String, subject, 0);
5060 CONVERT_ARG_CHECKED(String, pattern, 1);
5061 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5062
5063 int subject_length = subject->length();
5064 int pattern_length = pattern->length();
5065 RUNTIME_ASSERT(pattern_length > 0);
5066
5067 // The limit can be very large (0xffffffffu), but since the pattern
5068 // isn't empty, we can never create more parts than ~half the length
5069 // of the subject.
5070
5071 if (!subject->IsFlat()) FlattenString(subject);
5072
5073 static const int kMaxInitialListCapacity = 16;
5074
5075 ZoneScope scope(DELETE_ON_EXIT);
5076
5077 // Find (up to limit) indices of separator and end-of-string in subject
5078 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5079 ZoneList<int> indices(initial_capacity);
5080 if (pattern_length == 1) {
5081 // Special case, go directly to fast single-character split.
5082 AssertNoAllocation nogc;
5083 uc16 pattern_char = pattern->Get(0);
5084 if (subject->IsTwoByteRepresentation()) {
5085 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5086 &indices,
5087 limit);
5088 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5089 FindCharIndices(subject->ToAsciiVector(),
5090 static_cast<char>(pattern_char),
5091 &indices,
5092 limit);
5093 }
5094 } else {
5095 if (!pattern->IsFlat()) FlattenString(pattern);
5096 AssertNoAllocation nogc;
5097 if (subject->IsAsciiRepresentation()) {
5098 Vector<const char> subject_vector = subject->ToAsciiVector();
5099 if (pattern->IsAsciiRepresentation()) {
5100 FindStringIndices(subject_vector,
5101 pattern->ToAsciiVector(),
5102 &indices,
5103 limit);
5104 } else {
5105 FindStringIndices(subject_vector,
5106 pattern->ToUC16Vector(),
5107 &indices,
5108 limit);
5109 }
5110 } else {
5111 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5112 if (pattern->IsAsciiRepresentation()) {
5113 FindStringIndices(subject_vector,
5114 pattern->ToAsciiVector(),
5115 &indices,
5116 limit);
5117 } else {
5118 FindStringIndices(subject_vector,
5119 pattern->ToUC16Vector(),
5120 &indices,
5121 limit);
5122 }
5123 }
5124 }
5125 if (static_cast<uint32_t>(indices.length()) < limit) {
5126 indices.Add(subject_length);
5127 }
5128 // The list indices now contains the end of each part to create.
5129
5130
5131 // Create JSArray of substrings separated by separator.
5132 int part_count = indices.length();
5133
5134 Handle<JSArray> result = Factory::NewJSArray(part_count);
5135 result->set_length(Smi::FromInt(part_count));
5136
5137 ASSERT(result->HasFastElements());
5138
5139 if (part_count == 1 && indices.at(0) == subject_length) {
5140 FixedArray::cast(result->elements())->set(0, *subject);
5141 return *result;
5142 }
5143
5144 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5145 int part_start = 0;
5146 for (int i = 0; i < part_count; i++) {
5147 HandleScope local_loop_handle;
5148 int part_end = indices.at(i);
5149 Handle<String> substring =
5150 Factory::NewSubString(subject, part_start, part_end);
5151 elements->set(i, *substring);
5152 part_start = part_end + pattern_length;
5153 }
5154
5155 return *result;
5156}
5157
5158
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005159// Copies ascii characters to the given fixed array looking up
5160// one-char strings in the cache. Gives up on the first char that is
5161// not in the cache and fills the remainder with smi zeros. Returns
5162// the length of the successfully copied prefix.
5163static int CopyCachedAsciiCharsToArray(const char* chars,
5164 FixedArray* elements,
5165 int length) {
5166 AssertNoAllocation nogc;
5167 FixedArray* ascii_cache = Heap::single_character_string_cache();
5168 Object* undefined = Heap::undefined_value();
5169 int i;
5170 for (i = 0; i < length; ++i) {
5171 Object* value = ascii_cache->get(chars[i]);
5172 if (value == undefined) break;
5173 ASSERT(!Heap::InNewSpace(value));
5174 elements->set(i, value, SKIP_WRITE_BARRIER);
5175 }
5176 if (i < length) {
5177 ASSERT(Smi::FromInt(0) == 0);
5178 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5179 }
5180#ifdef DEBUG
5181 for (int j = 0; j < length; ++j) {
5182 Object* element = elements->get(j);
5183 ASSERT(element == Smi::FromInt(0) ||
5184 (element->IsString() && String::cast(element)->LooksValid()));
5185 }
5186#endif
5187 return i;
5188}
5189
5190
5191// Converts a String to JSArray.
5192// For example, "foo" => ["f", "o", "o"].
5193static Object* Runtime_StringToArray(Arguments args) {
5194 HandleScope scope;
5195 ASSERT(args.length() == 1);
5196 CONVERT_ARG_CHECKED(String, s, 0);
5197
5198 s->TryFlatten();
5199 const int length = s->length();
5200
5201 Handle<FixedArray> elements;
5202 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5203 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5204 if (obj->IsFailure()) return obj;
5205 elements = Handle<FixedArray>(FixedArray::cast(obj));
5206
5207 Vector<const char> chars = s->ToAsciiVector();
5208 // Note, this will initialize all elements (not only the prefix)
5209 // to prevent GC from seeing partially initialized array.
5210 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5211 *elements,
5212 length);
5213
5214 for (int i = num_copied_from_cache; i < length; ++i) {
5215 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5216 }
5217 } else {
5218 elements = Factory::NewFixedArray(length);
5219 for (int i = 0; i < length; ++i) {
5220 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5221 }
5222 }
5223
5224#ifdef DEBUG
5225 for (int i = 0; i < length; ++i) {
5226 ASSERT(String::cast(elements->get(i))->length() == 1);
5227 }
5228#endif
5229
5230 return *Factory::NewJSArrayWithElements(elements);
5231}
5232
5233
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005234bool Runtime::IsUpperCaseChar(uint16_t ch) {
5235 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5236 int char_length = to_upper_mapping.get(ch, 0, chars);
5237 return char_length == 0;
5238}
5239
5240
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005241static Object* Runtime_NumberToString(Arguments args) {
5242 NoHandleAllocation ha;
5243 ASSERT(args.length() == 1);
5244
5245 Object* number = args[0];
5246 RUNTIME_ASSERT(number->IsNumber());
5247
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005248 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005249}
5250
5251
5252static Object* Runtime_NumberToInteger(Arguments args) {
5253 NoHandleAllocation ha;
5254 ASSERT(args.length() == 1);
5255
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005256 CONVERT_DOUBLE_CHECKED(number, args[0]);
5257
5258 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5259 if (number > 0 && number <= Smi::kMaxValue) {
5260 return Smi::FromInt(static_cast<int>(number));
5261 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005262 return Heap::NumberFromDouble(DoubleToInteger(number));
5263}
5264
5265
5266static Object* Runtime_NumberToJSUint32(Arguments args) {
5267 NoHandleAllocation ha;
5268 ASSERT(args.length() == 1);
5269
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005270 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005271 return Heap::NumberFromUint32(number);
5272}
5273
5274
5275static Object* Runtime_NumberToJSInt32(Arguments args) {
5276 NoHandleAllocation ha;
5277 ASSERT(args.length() == 1);
5278
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005279 CONVERT_DOUBLE_CHECKED(number, args[0]);
5280
5281 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5282 if (number > 0 && number <= Smi::kMaxValue) {
5283 return Smi::FromInt(static_cast<int>(number));
5284 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005285 return Heap::NumberFromInt32(DoubleToInt32(number));
5286}
5287
5288
ager@chromium.org870a0b62008-11-04 11:43:05 +00005289// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5290// a small integer.
5291static Object* Runtime_NumberToSmi(Arguments args) {
5292 NoHandleAllocation ha;
5293 ASSERT(args.length() == 1);
5294
5295 Object* obj = args[0];
5296 if (obj->IsSmi()) {
5297 return obj;
5298 }
5299 if (obj->IsHeapNumber()) {
5300 double value = HeapNumber::cast(obj)->value();
5301 int int_value = FastD2I(value);
5302 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5303 return Smi::FromInt(int_value);
5304 }
5305 }
5306 return Heap::nan_value();
5307}
5308
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005309
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005310static Object* Runtime_NumberAdd(Arguments args) {
5311 NoHandleAllocation ha;
5312 ASSERT(args.length() == 2);
5313
5314 CONVERT_DOUBLE_CHECKED(x, args[0]);
5315 CONVERT_DOUBLE_CHECKED(y, args[1]);
5316 return Heap::AllocateHeapNumber(x + y);
5317}
5318
5319
5320static Object* Runtime_NumberSub(Arguments args) {
5321 NoHandleAllocation ha;
5322 ASSERT(args.length() == 2);
5323
5324 CONVERT_DOUBLE_CHECKED(x, args[0]);
5325 CONVERT_DOUBLE_CHECKED(y, args[1]);
5326 return Heap::AllocateHeapNumber(x - y);
5327}
5328
5329
5330static Object* Runtime_NumberMul(Arguments args) {
5331 NoHandleAllocation ha;
5332 ASSERT(args.length() == 2);
5333
5334 CONVERT_DOUBLE_CHECKED(x, args[0]);
5335 CONVERT_DOUBLE_CHECKED(y, args[1]);
5336 return Heap::AllocateHeapNumber(x * y);
5337}
5338
5339
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005340static Object* Runtime_NumberUnaryMinus(Arguments args) {
5341 NoHandleAllocation ha;
5342 ASSERT(args.length() == 1);
5343
5344 CONVERT_DOUBLE_CHECKED(x, args[0]);
5345 return Heap::AllocateHeapNumber(-x);
5346}
5347
5348
5349static Object* Runtime_NumberDiv(Arguments args) {
5350 NoHandleAllocation ha;
5351 ASSERT(args.length() == 2);
5352
5353 CONVERT_DOUBLE_CHECKED(x, args[0]);
5354 CONVERT_DOUBLE_CHECKED(y, args[1]);
5355 return Heap::NewNumberFromDouble(x / y);
5356}
5357
5358
5359static Object* Runtime_NumberMod(Arguments args) {
5360 NoHandleAllocation ha;
5361 ASSERT(args.length() == 2);
5362
5363 CONVERT_DOUBLE_CHECKED(x, args[0]);
5364 CONVERT_DOUBLE_CHECKED(y, args[1]);
5365
ager@chromium.org3811b432009-10-28 14:53:37 +00005366 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005367 // NewNumberFromDouble may return a Smi instead of a Number object
5368 return Heap::NewNumberFromDouble(x);
5369}
5370
5371
5372static Object* Runtime_StringAdd(Arguments args) {
5373 NoHandleAllocation ha;
5374 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005375 CONVERT_CHECKED(String, str1, args[0]);
5376 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005377 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005378 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005379}
5380
5381
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005382template<typename sinkchar>
5383static inline void StringBuilderConcatHelper(String* special,
5384 sinkchar* sink,
5385 FixedArray* fixed_array,
5386 int array_length) {
5387 int position = 0;
5388 for (int i = 0; i < array_length; i++) {
5389 Object* element = fixed_array->get(i);
5390 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005391 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005392 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005393 int pos;
5394 int len;
5395 if (encoded_slice > 0) {
5396 // Position and length encoded in one smi.
5397 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5398 len = StringBuilderSubstringLength::decode(encoded_slice);
5399 } else {
5400 // Position and length encoded in two smis.
5401 Object* obj = fixed_array->get(++i);
5402 ASSERT(obj->IsSmi());
5403 pos = Smi::cast(obj)->value();
5404 len = -encoded_slice;
5405 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005406 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005407 sink + position,
5408 pos,
5409 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005410 position += len;
5411 } else {
5412 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005413 int element_length = string->length();
5414 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005415 position += element_length;
5416 }
5417 }
5418}
5419
5420
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421static Object* Runtime_StringBuilderConcat(Arguments args) {
5422 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005423 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005424 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005425 if (!args[1]->IsSmi()) {
5426 Top::context()->mark_out_of_memory();
5427 return Failure::OutOfMemoryException();
5428 }
5429 int array_length = Smi::cast(args[1])->value();
5430 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005431
5432 // This assumption is used by the slice encoding in one or two smis.
5433 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5434
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005435 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005436 if (!array->HasFastElements()) {
5437 return Top::Throw(Heap::illegal_argument_symbol());
5438 }
5439 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005440 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005441 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005442 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005443
5444 if (array_length == 0) {
5445 return Heap::empty_string();
5446 } else if (array_length == 1) {
5447 Object* first = fixed_array->get(0);
5448 if (first->IsString()) return first;
5449 }
5450
ager@chromium.org5ec48922009-05-05 07:25:34 +00005451 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005452 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005453 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005454 for (int i = 0; i < array_length; i++) {
5455 Object* elt = fixed_array->get(i);
5456 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005457 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005458 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005459 if (len > 0) {
5460 // Position and length encoded in one smi.
5461 int pos = len >> 11;
5462 len &= 0x7ff;
5463 if (pos + len > special_length) {
5464 return Top::Throw(Heap::illegal_argument_symbol());
5465 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005466 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005467 } else {
5468 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005469 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005470 // Get the position and check that it is also a smi.
5471 i++;
5472 if (i >= array_length) {
5473 return Top::Throw(Heap::illegal_argument_symbol());
5474 }
5475 Object* pos = fixed_array->get(i);
5476 if (!pos->IsSmi()) {
5477 return Top::Throw(Heap::illegal_argument_symbol());
5478 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005479 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005480 } else if (elt->IsString()) {
5481 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005482 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005483 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005484 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005485 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005486 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005487 } else {
5488 return Top::Throw(Heap::illegal_argument_symbol());
5489 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005490 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005491 Top::context()->mark_out_of_memory();
5492 return Failure::OutOfMemoryException();
5493 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005494 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495 }
5496
5497 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005499
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005500 if (ascii) {
5501 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005502 if (object->IsFailure()) return object;
5503 SeqAsciiString* answer = SeqAsciiString::cast(object);
5504 StringBuilderConcatHelper(special,
5505 answer->GetChars(),
5506 fixed_array,
5507 array_length);
5508 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005509 } else {
5510 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005511 if (object->IsFailure()) return object;
5512 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5513 StringBuilderConcatHelper(special,
5514 answer->GetChars(),
5515 fixed_array,
5516 array_length);
5517 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005518 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005519}
5520
5521
5522static Object* Runtime_NumberOr(Arguments args) {
5523 NoHandleAllocation ha;
5524 ASSERT(args.length() == 2);
5525
5526 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5527 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5528 return Heap::NumberFromInt32(x | y);
5529}
5530
5531
5532static Object* Runtime_NumberAnd(Arguments args) {
5533 NoHandleAllocation ha;
5534 ASSERT(args.length() == 2);
5535
5536 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5537 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5538 return Heap::NumberFromInt32(x & y);
5539}
5540
5541
5542static Object* Runtime_NumberXor(Arguments args) {
5543 NoHandleAllocation ha;
5544 ASSERT(args.length() == 2);
5545
5546 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5547 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5548 return Heap::NumberFromInt32(x ^ y);
5549}
5550
5551
5552static Object* Runtime_NumberNot(Arguments args) {
5553 NoHandleAllocation ha;
5554 ASSERT(args.length() == 1);
5555
5556 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5557 return Heap::NumberFromInt32(~x);
5558}
5559
5560
5561static Object* Runtime_NumberShl(Arguments args) {
5562 NoHandleAllocation ha;
5563 ASSERT(args.length() == 2);
5564
5565 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5566 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5567 return Heap::NumberFromInt32(x << (y & 0x1f));
5568}
5569
5570
5571static Object* Runtime_NumberShr(Arguments args) {
5572 NoHandleAllocation ha;
5573 ASSERT(args.length() == 2);
5574
5575 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5576 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5577 return Heap::NumberFromUint32(x >> (y & 0x1f));
5578}
5579
5580
5581static Object* Runtime_NumberSar(Arguments args) {
5582 NoHandleAllocation ha;
5583 ASSERT(args.length() == 2);
5584
5585 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5586 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5587 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5588}
5589
5590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005591static Object* Runtime_NumberEquals(Arguments args) {
5592 NoHandleAllocation ha;
5593 ASSERT(args.length() == 2);
5594
5595 CONVERT_DOUBLE_CHECKED(x, args[0]);
5596 CONVERT_DOUBLE_CHECKED(y, args[1]);
5597 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5598 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5599 if (x == y) return Smi::FromInt(EQUAL);
5600 Object* result;
5601 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5602 result = Smi::FromInt(EQUAL);
5603 } else {
5604 result = Smi::FromInt(NOT_EQUAL);
5605 }
5606 return result;
5607}
5608
5609
5610static Object* Runtime_StringEquals(Arguments args) {
5611 NoHandleAllocation ha;
5612 ASSERT(args.length() == 2);
5613
5614 CONVERT_CHECKED(String, x, args[0]);
5615 CONVERT_CHECKED(String, y, args[1]);
5616
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005617 bool not_equal = !x->Equals(y);
5618 // This is slightly convoluted because the value that signifies
5619 // equality is 0 and inequality is 1 so we have to negate the result
5620 // from String::Equals.
5621 ASSERT(not_equal == 0 || not_equal == 1);
5622 STATIC_CHECK(EQUAL == 0);
5623 STATIC_CHECK(NOT_EQUAL == 1);
5624 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005625}
5626
5627
5628static Object* Runtime_NumberCompare(Arguments args) {
5629 NoHandleAllocation ha;
5630 ASSERT(args.length() == 3);
5631
5632 CONVERT_DOUBLE_CHECKED(x, args[0]);
5633 CONVERT_DOUBLE_CHECKED(y, args[1]);
5634 if (isnan(x) || isnan(y)) return args[2];
5635 if (x == y) return Smi::FromInt(EQUAL);
5636 if (isless(x, y)) return Smi::FromInt(LESS);
5637 return Smi::FromInt(GREATER);
5638}
5639
5640
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005641// Compare two Smis as if they were converted to strings and then
5642// compared lexicographically.
5643static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5644 NoHandleAllocation ha;
5645 ASSERT(args.length() == 2);
5646
5647 // Arrays for the individual characters of the two Smis. Smis are
5648 // 31 bit integers and 10 decimal digits are therefore enough.
5649 static int x_elms[10];
5650 static int y_elms[10];
5651
5652 // Extract the integer values from the Smis.
5653 CONVERT_CHECKED(Smi, x, args[0]);
5654 CONVERT_CHECKED(Smi, y, args[1]);
5655 int x_value = x->value();
5656 int y_value = y->value();
5657
5658 // If the integers are equal so are the string representations.
5659 if (x_value == y_value) return Smi::FromInt(EQUAL);
5660
5661 // If one of the integers are zero the normal integer order is the
5662 // same as the lexicographic order of the string representations.
5663 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5664
ager@chromium.org32912102009-01-16 10:38:43 +00005665 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005666 // smallest because the char code of '-' is less than the char code
5667 // of any digit. Otherwise, we make both values positive.
5668 if (x_value < 0 || y_value < 0) {
5669 if (y_value >= 0) return Smi::FromInt(LESS);
5670 if (x_value >= 0) return Smi::FromInt(GREATER);
5671 x_value = -x_value;
5672 y_value = -y_value;
5673 }
5674
5675 // Convert the integers to arrays of their decimal digits.
5676 int x_index = 0;
5677 int y_index = 0;
5678 while (x_value > 0) {
5679 x_elms[x_index++] = x_value % 10;
5680 x_value /= 10;
5681 }
5682 while (y_value > 0) {
5683 y_elms[y_index++] = y_value % 10;
5684 y_value /= 10;
5685 }
5686
5687 // Loop through the arrays of decimal digits finding the first place
5688 // where they differ.
5689 while (--x_index >= 0 && --y_index >= 0) {
5690 int diff = x_elms[x_index] - y_elms[y_index];
5691 if (diff != 0) return Smi::FromInt(diff);
5692 }
5693
5694 // If one array is a suffix of the other array, the longest array is
5695 // the representation of the largest of the Smis in the
5696 // lexicographic ordering.
5697 return Smi::FromInt(x_index - y_index);
5698}
5699
5700
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005701static Object* StringInputBufferCompare(String* x, String* y) {
5702 static StringInputBuffer bufx;
5703 static StringInputBuffer bufy;
5704 bufx.Reset(x);
5705 bufy.Reset(y);
5706 while (bufx.has_more() && bufy.has_more()) {
5707 int d = bufx.GetNext() - bufy.GetNext();
5708 if (d < 0) return Smi::FromInt(LESS);
5709 else if (d > 0) return Smi::FromInt(GREATER);
5710 }
5711
5712 // x is (non-trivial) prefix of y:
5713 if (bufy.has_more()) return Smi::FromInt(LESS);
5714 // y is prefix of x:
5715 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5716}
5717
5718
5719static Object* FlatStringCompare(String* x, String* y) {
5720 ASSERT(x->IsFlat());
5721 ASSERT(y->IsFlat());
5722 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5723 int prefix_length = x->length();
5724 if (y->length() < prefix_length) {
5725 prefix_length = y->length();
5726 equal_prefix_result = Smi::FromInt(GREATER);
5727 } else if (y->length() > prefix_length) {
5728 equal_prefix_result = Smi::FromInt(LESS);
5729 }
5730 int r;
5731 if (x->IsAsciiRepresentation()) {
5732 Vector<const char> x_chars = x->ToAsciiVector();
5733 if (y->IsAsciiRepresentation()) {
5734 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005735 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005736 } else {
5737 Vector<const uc16> y_chars = y->ToUC16Vector();
5738 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5739 }
5740 } else {
5741 Vector<const uc16> x_chars = x->ToUC16Vector();
5742 if (y->IsAsciiRepresentation()) {
5743 Vector<const char> y_chars = y->ToAsciiVector();
5744 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5745 } else {
5746 Vector<const uc16> y_chars = y->ToUC16Vector();
5747 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5748 }
5749 }
5750 Object* result;
5751 if (r == 0) {
5752 result = equal_prefix_result;
5753 } else {
5754 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5755 }
5756 ASSERT(result == StringInputBufferCompare(x, y));
5757 return result;
5758}
5759
5760
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005761static Object* Runtime_StringCompare(Arguments args) {
5762 NoHandleAllocation ha;
5763 ASSERT(args.length() == 2);
5764
5765 CONVERT_CHECKED(String, x, args[0]);
5766 CONVERT_CHECKED(String, y, args[1]);
5767
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005768 Counters::string_compare_runtime.Increment();
5769
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 // A few fast case tests before we flatten.
5771 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005772 if (y->length() == 0) {
5773 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005774 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005775 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005776 return Smi::FromInt(LESS);
5777 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005778
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005779 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005780 if (d < 0) return Smi::FromInt(LESS);
5781 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005782
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005783 Object* obj = Heap::PrepareForCompare(x);
5784 if (obj->IsFailure()) return obj;
5785 obj = Heap::PrepareForCompare(y);
5786 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005787
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005788 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5789 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005790}
5791
5792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793static Object* Runtime_Math_acos(Arguments args) {
5794 NoHandleAllocation ha;
5795 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005796 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005797
5798 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005799 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005800}
5801
5802
5803static Object* Runtime_Math_asin(Arguments args) {
5804 NoHandleAllocation ha;
5805 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005806 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005807
5808 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005809 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005810}
5811
5812
5813static Object* Runtime_Math_atan(Arguments args) {
5814 NoHandleAllocation ha;
5815 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005816 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005817
5818 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005819 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005820}
5821
5822
5823static Object* Runtime_Math_atan2(Arguments args) {
5824 NoHandleAllocation ha;
5825 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005826 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005827
5828 CONVERT_DOUBLE_CHECKED(x, args[0]);
5829 CONVERT_DOUBLE_CHECKED(y, args[1]);
5830 double result;
5831 if (isinf(x) && isinf(y)) {
5832 // Make sure that the result in case of two infinite arguments
5833 // is a multiple of Pi / 4. The sign of the result is determined
5834 // by the first argument (x) and the sign of the second argument
5835 // determines the multiplier: one or three.
5836 static double kPiDividedBy4 = 0.78539816339744830962;
5837 int multiplier = (x < 0) ? -1 : 1;
5838 if (y < 0) multiplier *= 3;
5839 result = multiplier * kPiDividedBy4;
5840 } else {
5841 result = atan2(x, y);
5842 }
5843 return Heap::AllocateHeapNumber(result);
5844}
5845
5846
5847static Object* Runtime_Math_ceil(Arguments args) {
5848 NoHandleAllocation ha;
5849 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005850 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005851
5852 CONVERT_DOUBLE_CHECKED(x, args[0]);
5853 return Heap::NumberFromDouble(ceiling(x));
5854}
5855
5856
5857static Object* Runtime_Math_cos(Arguments args) {
5858 NoHandleAllocation ha;
5859 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005860 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005861
5862 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005863 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005864}
5865
5866
5867static Object* Runtime_Math_exp(Arguments args) {
5868 NoHandleAllocation ha;
5869 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005870 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005871
5872 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005873 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005874}
5875
5876
5877static Object* Runtime_Math_floor(Arguments args) {
5878 NoHandleAllocation ha;
5879 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005880 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005881
5882 CONVERT_DOUBLE_CHECKED(x, args[0]);
5883 return Heap::NumberFromDouble(floor(x));
5884}
5885
5886
5887static Object* Runtime_Math_log(Arguments args) {
5888 NoHandleAllocation ha;
5889 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005890 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005891
5892 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005893 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005894}
5895
5896
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005897// Helper function to compute x^y, where y is known to be an
5898// integer. Uses binary decomposition to limit the number of
5899// multiplications; see the discussion in "Hacker's Delight" by Henry
5900// S. Warren, Jr., figure 11-6, page 213.
5901static double powi(double x, int y) {
5902 ASSERT(y != kMinInt);
5903 unsigned n = (y < 0) ? -y : y;
5904 double m = x;
5905 double p = 1;
5906 while (true) {
5907 if ((n & 1) != 0) p *= m;
5908 n >>= 1;
5909 if (n == 0) {
5910 if (y < 0) {
5911 // Unfortunately, we have to be careful when p has reached
5912 // infinity in the computation, because sometimes the higher
5913 // internal precision in the pow() implementation would have
5914 // given us a finite p. This happens very rarely.
5915 double result = 1.0 / p;
5916 return (result == 0 && isinf(p))
5917 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
5918 : result;
5919 } else {
5920 return p;
5921 }
5922 }
5923 m *= m;
5924 }
5925}
5926
5927
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005928static Object* Runtime_Math_pow(Arguments args) {
5929 NoHandleAllocation ha;
5930 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005931 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005932
5933 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005934
5935 // If the second argument is a smi, it is much faster to call the
5936 // custom powi() function than the generic pow().
5937 if (args[1]->IsSmi()) {
5938 int y = Smi::cast(args[1])->value();
5939 return Heap::AllocateHeapNumber(powi(x, y));
5940 }
5941
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005942 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005943
5944 if (!isinf(x)) {
5945 if (y == 0.5) {
5946 // It's not uncommon to use Math.pow(x, 0.5) to compute the
5947 // square root of a number. To speed up such computations, we
5948 // explictly check for this case and use the sqrt() function
5949 // which is faster than pow().
5950 return Heap::AllocateHeapNumber(sqrt(x));
5951 } else if (y == -0.5) {
5952 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
5953 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
5954 }
5955 }
5956
5957 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005958 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005959 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5960 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005961 } else {
5962 return Heap::AllocateHeapNumber(pow(x, y));
5963 }
5964}
5965
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005966// Fast version of Math.pow if we know that y is not an integer and
5967// y is not -0.5 or 0.5. Used as slowcase from codegen.
5968static Object* Runtime_Math_pow_cfunction(Arguments args) {
5969 NoHandleAllocation ha;
5970 ASSERT(args.length() == 2);
5971 CONVERT_DOUBLE_CHECKED(x, args[0]);
5972 CONVERT_DOUBLE_CHECKED(y, args[1]);
5973 if (y == 0) {
5974 return Smi::FromInt(1);
5975 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5976 return Heap::nan_value();
5977 } else {
5978 return Heap::AllocateHeapNumber(pow(x, y));
5979 }
5980}
5981
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005982
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00005983static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005984 NoHandleAllocation ha;
5985 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005986 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005987
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00005988 if (!args[0]->IsHeapNumber()) {
5989 // Must be smi. Return the argument unchanged for all the other types
5990 // to make fuzz-natives test happy.
5991 return args[0];
5992 }
5993
5994 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
5995
5996 double value = number->value();
5997 int exponent = number->get_exponent();
5998 int sign = number->get_sign();
5999
6000 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6001 // should be rounded to 2^30, which is not smi.
6002 if (!sign && exponent <= kSmiValueSize - 3) {
6003 return Smi::FromInt(static_cast<int>(value + 0.5));
6004 }
6005
6006 // If the magnitude is big enough, there's no place for fraction part. If we
6007 // try to add 0.5 to this number, 1.0 will be added instead.
6008 if (exponent >= 52) {
6009 return number;
6010 }
6011
6012 if (sign && value >= -0.5) return Heap::minus_zero_value();
6013
6014 return Heap::NumberFromDouble(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006015}
6016
6017
6018static Object* Runtime_Math_sin(Arguments args) {
6019 NoHandleAllocation ha;
6020 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006021 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006022
6023 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006024 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006025}
6026
6027
6028static Object* Runtime_Math_sqrt(Arguments args) {
6029 NoHandleAllocation ha;
6030 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006031 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006032
6033 CONVERT_DOUBLE_CHECKED(x, args[0]);
6034 return Heap::AllocateHeapNumber(sqrt(x));
6035}
6036
6037
6038static Object* Runtime_Math_tan(Arguments args) {
6039 NoHandleAllocation ha;
6040 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006041 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006042
6043 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006044 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006045}
6046
6047
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006048static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006049 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6050 181, 212, 243, 273, 304, 334};
6051 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6052 182, 213, 244, 274, 305, 335};
6053
6054 year += month / 12;
6055 month %= 12;
6056 if (month < 0) {
6057 year--;
6058 month += 12;
6059 }
6060
6061 ASSERT(month >= 0);
6062 ASSERT(month < 12);
6063
6064 // year_delta is an arbitrary number such that:
6065 // a) year_delta = -1 (mod 400)
6066 // b) year + year_delta > 0 for years in the range defined by
6067 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6068 // Jan 1 1970. This is required so that we don't run into integer
6069 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006070 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006071 // operations.
6072 static const int year_delta = 399999;
6073 static const int base_day = 365 * (1970 + year_delta) +
6074 (1970 + year_delta) / 4 -
6075 (1970 + year_delta) / 100 +
6076 (1970 + year_delta) / 400;
6077
6078 int year1 = year + year_delta;
6079 int day_from_year = 365 * year1 +
6080 year1 / 4 -
6081 year1 / 100 +
6082 year1 / 400 -
6083 base_day;
6084
6085 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006086 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006087 }
6088
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006089 return day_from_year + day_from_month_leap[month] + day - 1;
6090}
6091
6092
6093static Object* Runtime_DateMakeDay(Arguments args) {
6094 NoHandleAllocation ha;
6095 ASSERT(args.length() == 3);
6096
6097 CONVERT_SMI_CHECKED(year, args[0]);
6098 CONVERT_SMI_CHECKED(month, args[1]);
6099 CONVERT_SMI_CHECKED(date, args[2]);
6100
6101 return Smi::FromInt(MakeDay(year, month, date));
6102}
6103
6104
6105static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6106static const int kDaysIn4Years = 4 * 365 + 1;
6107static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6108static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6109static const int kDays1970to2000 = 30 * 365 + 7;
6110static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6111 kDays1970to2000;
6112static const int kYearsOffset = 400000;
6113
6114static const char kDayInYear[] = {
6115 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6116 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6117 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6118 22, 23, 24, 25, 26, 27, 28,
6119 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6120 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6121 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6122 22, 23, 24, 25, 26, 27, 28, 29, 30,
6123 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6124 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6125 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6126 22, 23, 24, 25, 26, 27, 28, 29, 30,
6127 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6128 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6129 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6130 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6131 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6132 22, 23, 24, 25, 26, 27, 28, 29, 30,
6133 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6134 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6135 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6136 22, 23, 24, 25, 26, 27, 28, 29, 30,
6137 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6138 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6139
6140 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6141 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6142 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6143 22, 23, 24, 25, 26, 27, 28,
6144 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6145 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6146 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6147 22, 23, 24, 25, 26, 27, 28, 29, 30,
6148 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6149 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6150 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6151 22, 23, 24, 25, 26, 27, 28, 29, 30,
6152 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6153 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6154 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6155 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6156 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6157 22, 23, 24, 25, 26, 27, 28, 29, 30,
6158 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6159 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6160 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6161 22, 23, 24, 25, 26, 27, 28, 29, 30,
6162 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6163 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6164
6165 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6166 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6167 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6168 22, 23, 24, 25, 26, 27, 28, 29,
6169 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6170 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6171 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6172 22, 23, 24, 25, 26, 27, 28, 29, 30,
6173 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6174 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6175 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6176 22, 23, 24, 25, 26, 27, 28, 29, 30,
6177 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6178 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6179 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6180 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6181 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6182 22, 23, 24, 25, 26, 27, 28, 29, 30,
6183 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6184 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6185 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6186 22, 23, 24, 25, 26, 27, 28, 29, 30,
6187 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6188 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6189
6190 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6191 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6192 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6193 22, 23, 24, 25, 26, 27, 28,
6194 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6195 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6196 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6197 22, 23, 24, 25, 26, 27, 28, 29, 30,
6198 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6199 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6200 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6201 22, 23, 24, 25, 26, 27, 28, 29, 30,
6202 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6203 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6204 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6205 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6206 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6207 22, 23, 24, 25, 26, 27, 28, 29, 30,
6208 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6209 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6210 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6211 22, 23, 24, 25, 26, 27, 28, 29, 30,
6212 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6213 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6214
6215static const char kMonthInYear[] = {
6216 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6217 0, 0, 0, 0, 0, 0,
6218 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6219 1, 1, 1,
6220 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6221 2, 2, 2, 2, 2, 2,
6222 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6223 3, 3, 3, 3, 3,
6224 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6225 4, 4, 4, 4, 4, 4,
6226 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6227 5, 5, 5, 5, 5,
6228 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6229 6, 6, 6, 6, 6, 6,
6230 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6231 7, 7, 7, 7, 7, 7,
6232 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6233 8, 8, 8, 8, 8,
6234 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6235 9, 9, 9, 9, 9, 9,
6236 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6237 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6238 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6239 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6240
6241 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6242 0, 0, 0, 0, 0, 0,
6243 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6244 1, 1, 1,
6245 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6246 2, 2, 2, 2, 2, 2,
6247 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6248 3, 3, 3, 3, 3,
6249 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6250 4, 4, 4, 4, 4, 4,
6251 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6252 5, 5, 5, 5, 5,
6253 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6254 6, 6, 6, 6, 6, 6,
6255 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6256 7, 7, 7, 7, 7, 7,
6257 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6258 8, 8, 8, 8, 8,
6259 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6260 9, 9, 9, 9, 9, 9,
6261 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6262 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6263 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6264 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6265
6266 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6267 0, 0, 0, 0, 0, 0,
6268 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6269 1, 1, 1, 1,
6270 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6271 2, 2, 2, 2, 2, 2,
6272 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6273 3, 3, 3, 3, 3,
6274 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6275 4, 4, 4, 4, 4, 4,
6276 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6277 5, 5, 5, 5, 5,
6278 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6279 6, 6, 6, 6, 6, 6,
6280 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6281 7, 7, 7, 7, 7, 7,
6282 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6283 8, 8, 8, 8, 8,
6284 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6285 9, 9, 9, 9, 9, 9,
6286 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6287 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6288 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6289 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6290
6291 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6292 0, 0, 0, 0, 0, 0,
6293 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
6294 1, 1, 1,
6295 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
6296 2, 2, 2, 2, 2, 2,
6297 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
6298 3, 3, 3, 3, 3,
6299 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
6300 4, 4, 4, 4, 4, 4,
6301 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6302 5, 5, 5, 5, 5,
6303 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6304 6, 6, 6, 6, 6, 6,
6305 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
6306 7, 7, 7, 7, 7, 7,
6307 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
6308 8, 8, 8, 8, 8,
6309 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
6310 9, 9, 9, 9, 9, 9,
6311 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6312 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6313 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6314 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6315
6316
6317// This function works for dates from 1970 to 2099.
6318static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006319 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006320#ifdef DEBUG
6321 int save_date = date; // Need this for ASSERT in the end.
6322#endif
6323
6324 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6325 date %= kDaysIn4Years;
6326
6327 month = kMonthInYear[date];
6328 day = kDayInYear[date];
6329
6330 ASSERT(MakeDay(year, month, day) == save_date);
6331}
6332
6333
6334static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006335 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006336#ifdef DEBUG
6337 int save_date = date; // Need this for ASSERT in the end.
6338#endif
6339
6340 date += kDaysOffset;
6341 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6342 date %= kDaysIn400Years;
6343
6344 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6345
6346 date--;
6347 int yd1 = date / kDaysIn100Years;
6348 date %= kDaysIn100Years;
6349 year += 100 * yd1;
6350
6351 date++;
6352 int yd2 = date / kDaysIn4Years;
6353 date %= kDaysIn4Years;
6354 year += 4 * yd2;
6355
6356 date--;
6357 int yd3 = date / 365;
6358 date %= 365;
6359 year += yd3;
6360
6361 bool is_leap = (!yd1 || yd2) && !yd3;
6362
6363 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006364 ASSERT(is_leap || (date >= 0));
6365 ASSERT((date < 365) || (is_leap && (date < 366)));
6366 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6367 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6368 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006369
6370 if (is_leap) {
6371 day = kDayInYear[2*365 + 1 + date];
6372 month = kMonthInYear[2*365 + 1 + date];
6373 } else {
6374 day = kDayInYear[date];
6375 month = kMonthInYear[date];
6376 }
6377
6378 ASSERT(MakeDay(year, month, day) == save_date);
6379}
6380
6381
6382static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006383 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006384 if (date >= 0 && date < 32 * kDaysIn4Years) {
6385 DateYMDFromTimeAfter1970(date, year, month, day);
6386 } else {
6387 DateYMDFromTimeSlow(date, year, month, day);
6388 }
6389}
6390
6391
6392static Object* Runtime_DateYMDFromTime(Arguments args) {
6393 NoHandleAllocation ha;
6394 ASSERT(args.length() == 2);
6395
6396 CONVERT_DOUBLE_CHECKED(t, args[0]);
6397 CONVERT_CHECKED(JSArray, res_array, args[1]);
6398
6399 int year, month, day;
6400 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6401
6402 res_array->SetElement(0, Smi::FromInt(year));
6403 res_array->SetElement(1, Smi::FromInt(month));
6404 res_array->SetElement(2, Smi::FromInt(day));
6405
6406 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006407}
6408
6409
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006410static Object* Runtime_NewArgumentsFast(Arguments args) {
6411 NoHandleAllocation ha;
6412 ASSERT(args.length() == 3);
6413
6414 JSFunction* callee = JSFunction::cast(args[0]);
6415 Object** parameters = reinterpret_cast<Object**>(args[1]);
6416 const int length = Smi::cast(args[2])->value();
6417
6418 Object* result = Heap::AllocateArgumentsObject(callee, length);
6419 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006420 // Allocate the elements if needed.
6421 if (length > 0) {
6422 // Allocate the fixed array.
6423 Object* obj = Heap::AllocateRawFixedArray(length);
6424 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006425
6426 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006427 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
6428 FixedArray* array = FixedArray::cast(obj);
6429 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006430
6431 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006432 for (int i = 0; i < length; i++) {
6433 array->set(i, *--parameters, mode);
6434 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006435 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006436 }
6437 return result;
6438}
6439
6440
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006441static Object* Runtime_NewClosure(Arguments args) {
6442 HandleScope scope;
6443 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006444 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006445 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006446
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006447 PretenureFlag pretenure = (context->global_context() == *context)
6448 ? TENURED // Allocate global closures in old space.
6449 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006450 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006451 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006452 return *result;
6453}
6454
6455
ager@chromium.org5c838252010-02-19 08:53:10 +00006456static Code* ComputeConstructStub(Handle<JSFunction> function) {
6457 Handle<Object> prototype = Factory::null_value();
6458 if (function->has_instance_prototype()) {
6459 prototype = Handle<Object>(function->instance_prototype());
6460 }
6461 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006462 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006463 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006464 if (code->IsFailure()) {
6465 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6466 }
6467 return Code::cast(code);
6468 }
6469
ager@chromium.org5c838252010-02-19 08:53:10 +00006470 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006471}
6472
6473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006474static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006475 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006476 ASSERT(args.length() == 1);
6477
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006478 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006479
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006480 // If the constructor isn't a proper function we throw a type error.
6481 if (!constructor->IsJSFunction()) {
6482 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6483 Handle<Object> type_error =
6484 Factory::NewTypeError("not_constructor", arguments);
6485 return Top::Throw(*type_error);
6486 }
6487
6488 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006489#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006490 // Handle stepping into constructors if step into is active.
6491 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006492 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006493 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006494#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006495
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006496 if (function->has_initial_map()) {
6497 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006498 // The 'Function' function ignores the receiver object when
6499 // called using 'new' and creates a new JSFunction object that
6500 // is returned. The receiver object is only used for error
6501 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006502 // JSFunction. Factory::NewJSObject() should not be used to
6503 // allocate JSFunctions since it does not properly initialize
6504 // the shared part of the function. Since the receiver is
6505 // ignored anyway, we use the global object as the receiver
6506 // instead of a new JSFunction object. This way, errors are
6507 // reported the same way whether or not 'Function' is called
6508 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006509 return Top::context()->global();
6510 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006511 }
6512
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006513 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006514 Handle<SharedFunctionInfo> shared(function->shared());
6515 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006516
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006517 bool first_allocation = !function->has_initial_map();
6518 Handle<JSObject> result = Factory::NewJSObject(function);
6519 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006520 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006521 ComputeConstructStub(Handle<JSFunction>(function)));
6522 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006523 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006524
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006525 Counters::constructed_objects.Increment();
6526 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006527
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006528 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006529}
6530
6531
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006532static Object* Runtime_LazyCompile(Arguments args) {
6533 HandleScope scope;
6534 ASSERT(args.length() == 1);
6535
6536 Handle<JSFunction> function = args.at<JSFunction>(0);
6537#ifdef DEBUG
6538 if (FLAG_trace_lazy) {
6539 PrintF("[lazy: ");
6540 function->shared()->name()->Print();
6541 PrintF("]\n");
6542 }
6543#endif
6544
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006545 // Compile the target function. Here we compile using CompileLazyInLoop in
6546 // order to get the optimized version. This helps code like delta-blue
6547 // that calls performance-critical routines through constructors. A
6548 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6549 // direct call. Since the in-loop tracking takes place through CallICs
6550 // this means that things called through constructors are never known to
6551 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006552 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006553 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006554 return Failure::Exception();
6555 }
6556
6557 return function->code();
6558}
6559
6560
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006561static Object* Runtime_GetFunctionDelegate(Arguments args) {
6562 HandleScope scope;
6563 ASSERT(args.length() == 1);
6564 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6565 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6566}
6567
6568
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006569static Object* Runtime_GetConstructorDelegate(Arguments args) {
6570 HandleScope scope;
6571 ASSERT(args.length() == 1);
6572 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6573 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6574}
6575
6576
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006577static Object* Runtime_NewContext(Arguments args) {
6578 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006579 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006580
kasper.lund7276f142008-07-30 08:49:36 +00006581 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006582 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6583 Object* result = Heap::AllocateFunctionContext(length, function);
6584 if (result->IsFailure()) return result;
6585
6586 Top::set_context(Context::cast(result));
6587
kasper.lund7276f142008-07-30 08:49:36 +00006588 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006589}
6590
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006591static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006592 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006593 Object* js_object = object;
6594 if (!js_object->IsJSObject()) {
6595 js_object = js_object->ToObject();
6596 if (js_object->IsFailure()) {
6597 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006598 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006599 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006600 Handle<Object> result =
6601 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6602 return Top::Throw(*result);
6603 }
6604 }
6605
6606 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006607 Heap::AllocateWithContext(Top::context(),
6608 JSObject::cast(js_object),
6609 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006610 if (result->IsFailure()) return result;
6611
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006612 Context* context = Context::cast(result);
6613 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006614
kasper.lund7276f142008-07-30 08:49:36 +00006615 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006616}
6617
6618
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006619static Object* Runtime_PushContext(Arguments args) {
6620 NoHandleAllocation ha;
6621 ASSERT(args.length() == 1);
6622 return PushContextHelper(args[0], false);
6623}
6624
6625
6626static Object* Runtime_PushCatchContext(Arguments args) {
6627 NoHandleAllocation ha;
6628 ASSERT(args.length() == 1);
6629 return PushContextHelper(args[0], true);
6630}
6631
6632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006633static Object* Runtime_LookupContext(Arguments args) {
6634 HandleScope scope;
6635 ASSERT(args.length() == 2);
6636
6637 CONVERT_ARG_CHECKED(Context, context, 0);
6638 CONVERT_ARG_CHECKED(String, name, 1);
6639
6640 int index;
6641 PropertyAttributes attributes;
6642 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006643 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006644 context->Lookup(name, flags, &index, &attributes);
6645
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006646 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006647 ASSERT(holder->IsJSObject());
6648 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006649 }
6650
6651 // No intermediate context found. Use global object by default.
6652 return Top::context()->global();
6653}
6654
6655
ager@chromium.orga1645e22009-09-09 19:27:10 +00006656// A mechanism to return a pair of Object pointers in registers (if possible).
6657// How this is achieved is calling convention-dependent.
6658// All currently supported x86 compiles uses calling conventions that are cdecl
6659// variants where a 64-bit value is returned in two 32-bit registers
6660// (edx:eax on ia32, r1:r0 on ARM).
6661// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6662// In Win64 calling convention, a struct of two pointers is returned in memory,
6663// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006664#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006665struct ObjectPair {
6666 Object* x;
6667 Object* y;
6668};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006669
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006670static inline ObjectPair MakePair(Object* x, Object* y) {
6671 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006672 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6673 // In Win64 they are assigned to a hidden first argument.
6674 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006675}
6676#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006677typedef uint64_t ObjectPair;
6678static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006679 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006680 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006681}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006682#endif
6683
6684
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006685static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006686 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6687 USE(attributes);
6688 return x->IsTheHole() ? Heap::undefined_value() : x;
6689}
6690
6691
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006692static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6693 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006694 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006695 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006696 JSFunction* context_extension_function =
6697 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006698 // If the holder isn't a context extension object, we just return it
6699 // as the receiver. This allows arguments objects to be used as
6700 // receivers, but only if they are put in the context scope chain
6701 // explicitly via a with-statement.
6702 Object* constructor = holder->map()->constructor();
6703 if (constructor != context_extension_function) return holder;
6704 // Fall back to using the global object as the receiver if the
6705 // property turns out to be a local variable allocated in a context
6706 // extension object - introduced via eval.
6707 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006708}
6709
6710
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006711static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006712 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006713 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006714
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006715 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006716 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006717 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006718 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006719 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006720
6721 int index;
6722 PropertyAttributes attributes;
6723 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006724 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006725 context->Lookup(name, flags, &index, &attributes);
6726
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006727 // If the index is non-negative, the slot has been found in a local
6728 // variable or a parameter. Read it from the context object or the
6729 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006730 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006731 // If the "property" we were looking for is a local variable or an
6732 // argument in a context, the receiver is the global object; see
6733 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6734 JSObject* receiver = Top::context()->global()->global_receiver();
6735 Object* value = (holder->IsContext())
6736 ? Context::cast(*holder)->get(index)
6737 : JSObject::cast(*holder)->GetElement(index);
6738 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006739 }
6740
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006741 // If the holder is found, we read the property from it.
6742 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006743 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006744 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006745 JSObject* receiver;
6746 if (object->IsGlobalObject()) {
6747 receiver = GlobalObject::cast(object)->global_receiver();
6748 } else if (context->is_exception_holder(*holder)) {
6749 receiver = Top::context()->global()->global_receiver();
6750 } else {
6751 receiver = ComputeReceiverForNonGlobal(object);
6752 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006753 // No need to unhole the value here. This is taken care of by the
6754 // GetProperty function.
6755 Object* value = object->GetProperty(*name);
6756 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006757 }
6758
6759 if (throw_error) {
6760 // The property doesn't exist - throw exception.
6761 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006762 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006763 return MakePair(Top::Throw(*reference_error), NULL);
6764 } else {
6765 // The property doesn't exist - return undefined
6766 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6767 }
6768}
6769
6770
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006771static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006772 return LoadContextSlotHelper(args, true);
6773}
6774
6775
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006776static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006777 return LoadContextSlotHelper(args, false);
6778}
6779
6780
6781static Object* Runtime_StoreContextSlot(Arguments args) {
6782 HandleScope scope;
6783 ASSERT(args.length() == 3);
6784
6785 Handle<Object> value(args[0]);
6786 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006787 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006788
6789 int index;
6790 PropertyAttributes attributes;
6791 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006792 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793 context->Lookup(name, flags, &index, &attributes);
6794
6795 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006796 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006797 // Ignore if read_only variable.
6798 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006799 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006800 }
6801 } else {
6802 ASSERT((attributes & READ_ONLY) == 0);
6803 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006804 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006805 USE(result);
6806 ASSERT(!result->IsFailure());
6807 }
6808 return *value;
6809 }
6810
6811 // Slow case: The property is not in a FixedArray context.
6812 // It is either in an JSObject extension context or it was not found.
6813 Handle<JSObject> context_ext;
6814
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006815 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006816 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006817 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006818 } else {
6819 // The property was not found. It needs to be stored in the global context.
6820 ASSERT(attributes == ABSENT);
6821 attributes = NONE;
6822 context_ext = Handle<JSObject>(Top::context()->global());
6823 }
6824
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006825 // Set the property, but ignore if read_only variable on the context
6826 // extension object itself.
6827 if ((attributes & READ_ONLY) == 0 ||
6828 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006829 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6830 if (set.is_null()) {
6831 // Failure::Exception is converted to a null handle in the
6832 // handle-based methods such as SetProperty. We therefore need
6833 // to convert null handles back to exceptions.
6834 ASSERT(Top::has_pending_exception());
6835 return Failure::Exception();
6836 }
6837 }
6838 return *value;
6839}
6840
6841
6842static Object* Runtime_Throw(Arguments args) {
6843 HandleScope scope;
6844 ASSERT(args.length() == 1);
6845
6846 return Top::Throw(args[0]);
6847}
6848
6849
6850static Object* Runtime_ReThrow(Arguments args) {
6851 HandleScope scope;
6852 ASSERT(args.length() == 1);
6853
6854 return Top::ReThrow(args[0]);
6855}
6856
6857
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006858static Object* Runtime_PromoteScheduledException(Arguments args) {
6859 ASSERT_EQ(0, args.length());
6860 return Top::PromoteScheduledException();
6861}
6862
6863
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006864static Object* Runtime_ThrowReferenceError(Arguments args) {
6865 HandleScope scope;
6866 ASSERT(args.length() == 1);
6867
6868 Handle<Object> name(args[0]);
6869 Handle<Object> reference_error =
6870 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6871 return Top::Throw(*reference_error);
6872}
6873
6874
6875static Object* Runtime_StackOverflow(Arguments args) {
6876 NoHandleAllocation na;
6877 return Top::StackOverflow();
6878}
6879
6880
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006881static Object* Runtime_StackGuard(Arguments args) {
6882 ASSERT(args.length() == 1);
6883
6884 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006885 if (StackGuard::IsStackOverflow()) {
6886 return Runtime_StackOverflow(args);
6887 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006888
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006889 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006890}
6891
6892
6893// NOTE: These PrintXXX functions are defined for all builds (not just
6894// DEBUG builds) because we may want to be able to trace function
6895// calls in all modes.
6896static void PrintString(String* str) {
6897 // not uncommon to have empty strings
6898 if (str->length() > 0) {
6899 SmartPointer<char> s =
6900 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
6901 PrintF("%s", *s);
6902 }
6903}
6904
6905
6906static void PrintObject(Object* obj) {
6907 if (obj->IsSmi()) {
6908 PrintF("%d", Smi::cast(obj)->value());
6909 } else if (obj->IsString() || obj->IsSymbol()) {
6910 PrintString(String::cast(obj));
6911 } else if (obj->IsNumber()) {
6912 PrintF("%g", obj->Number());
6913 } else if (obj->IsFailure()) {
6914 PrintF("<failure>");
6915 } else if (obj->IsUndefined()) {
6916 PrintF("<undefined>");
6917 } else if (obj->IsNull()) {
6918 PrintF("<null>");
6919 } else if (obj->IsTrue()) {
6920 PrintF("<true>");
6921 } else if (obj->IsFalse()) {
6922 PrintF("<false>");
6923 } else {
6924 PrintF("%p", obj);
6925 }
6926}
6927
6928
6929static int StackSize() {
6930 int n = 0;
6931 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
6932 return n;
6933}
6934
6935
6936static void PrintTransition(Object* result) {
6937 // indentation
6938 { const int nmax = 80;
6939 int n = StackSize();
6940 if (n <= nmax)
6941 PrintF("%4d:%*s", n, n, "");
6942 else
6943 PrintF("%4d:%*s", n, nmax, "...");
6944 }
6945
6946 if (result == NULL) {
6947 // constructor calls
6948 JavaScriptFrameIterator it;
6949 JavaScriptFrame* frame = it.frame();
6950 if (frame->IsConstructor()) PrintF("new ");
6951 // function name
6952 Object* fun = frame->function();
6953 if (fun->IsJSFunction()) {
6954 PrintObject(JSFunction::cast(fun)->shared()->name());
6955 } else {
6956 PrintObject(fun);
6957 }
6958 // function arguments
6959 // (we are intentionally only printing the actually
6960 // supplied parameters, not all parameters required)
6961 PrintF("(this=");
6962 PrintObject(frame->receiver());
6963 const int length = frame->GetProvidedParametersCount();
6964 for (int i = 0; i < length; i++) {
6965 PrintF(", ");
6966 PrintObject(frame->GetParameter(i));
6967 }
6968 PrintF(") {\n");
6969
6970 } else {
6971 // function result
6972 PrintF("} -> ");
6973 PrintObject(result);
6974 PrintF("\n");
6975 }
6976}
6977
6978
6979static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006980 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006981 NoHandleAllocation ha;
6982 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006983 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006984}
6985
6986
6987static Object* Runtime_TraceExit(Arguments args) {
6988 NoHandleAllocation ha;
6989 PrintTransition(args[0]);
6990 return args[0]; // return TOS
6991}
6992
6993
6994static Object* Runtime_DebugPrint(Arguments args) {
6995 NoHandleAllocation ha;
6996 ASSERT(args.length() == 1);
6997
6998#ifdef DEBUG
6999 if (args[0]->IsString()) {
7000 // If we have a string, assume it's a code "marker"
7001 // and print some interesting cpu debugging info.
7002 JavaScriptFrameIterator it;
7003 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007004 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7005 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007006 } else {
7007 PrintF("DebugPrint: ");
7008 }
7009 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007010 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007011 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007012 HeapObject::cast(args[0])->map()->Print();
7013 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007014#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007015 // ShortPrint is available in release mode. Print is not.
7016 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007017#endif
7018 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007019 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007020
7021 return args[0]; // return TOS
7022}
7023
7024
7025static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007026 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007027 NoHandleAllocation ha;
7028 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007029 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007030}
7031
7032
mads.s.ager31e71382008-08-13 09:32:07 +00007033static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007034 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007035 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007036
7037 // According to ECMA-262, section 15.9.1, page 117, the precision of
7038 // the number in a Date object representing a particular instant in
7039 // time is milliseconds. Therefore, we floor the result of getting
7040 // the OS time.
7041 double millis = floor(OS::TimeCurrentMillis());
7042 return Heap::NumberFromDouble(millis);
7043}
7044
7045
7046static Object* Runtime_DateParseString(Arguments args) {
7047 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007048 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007049
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007050 CONVERT_ARG_CHECKED(String, str, 0);
7051 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007052
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007053 CONVERT_ARG_CHECKED(JSArray, output, 1);
7054 RUNTIME_ASSERT(output->HasFastElements());
7055
7056 AssertNoAllocation no_allocation;
7057
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007058 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007059 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7060 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007061 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007062 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007063 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007064 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007065 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7066 }
7067
7068 if (result) {
7069 return *output;
7070 } else {
7071 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007072 }
7073}
7074
7075
7076static Object* Runtime_DateLocalTimezone(Arguments args) {
7077 NoHandleAllocation ha;
7078 ASSERT(args.length() == 1);
7079
7080 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007081 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007082 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7083}
7084
7085
7086static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7087 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007088 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007089
7090 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7091}
7092
7093
7094static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7095 NoHandleAllocation ha;
7096 ASSERT(args.length() == 1);
7097
7098 CONVERT_DOUBLE_CHECKED(x, args[0]);
7099 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7100}
7101
7102
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007103static Object* Runtime_GlobalReceiver(Arguments args) {
7104 ASSERT(args.length() == 1);
7105 Object* global = args[0];
7106 if (!global->IsJSGlobalObject()) return Heap::null_value();
7107 return JSGlobalObject::cast(global)->global_receiver();
7108}
7109
7110
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007111static Object* Runtime_CompileString(Arguments args) {
7112 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007113 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007114 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007115 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007116
ager@chromium.org381abbb2009-02-25 13:23:22 +00007117 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007118 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007119 Compiler::ValidationState validate = (is_json->IsTrue())
7120 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007121 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7122 context,
7123 true,
7124 validate);
7125 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007126 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007127 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007128 return *fun;
7129}
7130
7131
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007132static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7133 ASSERT(args.length() == 3);
7134 if (!args[0]->IsJSFunction()) {
7135 return MakePair(Top::ThrowIllegalOperation(), NULL);
7136 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007137
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007138 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007139 Handle<JSFunction> callee = args.at<JSFunction>(0);
7140 Handle<Object> receiver; // Will be overwritten.
7141
7142 // Compute the calling context.
7143 Handle<Context> context = Handle<Context>(Top::context());
7144#ifdef DEBUG
7145 // Make sure Top::context() agrees with the old code that traversed
7146 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007147 StackFrameLocator locator;
7148 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007149 ASSERT(Context::cast(frame->context()) == *context);
7150#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007151
7152 // Find where the 'eval' symbol is bound. It is unaliased only if
7153 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007154 int index = -1;
7155 PropertyAttributes attributes = ABSENT;
7156 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007157 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7158 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007159 // Stop search when eval is found or when the global context is
7160 // reached.
7161 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007162 if (context->is_function_context()) {
7163 context = Handle<Context>(Context::cast(context->closure()->context()));
7164 } else {
7165 context = Handle<Context>(context->previous());
7166 }
7167 }
7168
iposva@chromium.org245aa852009-02-10 00:49:54 +00007169 // If eval could not be resolved, it has been deleted and we need to
7170 // throw a reference error.
7171 if (attributes == ABSENT) {
7172 Handle<Object> name = Factory::eval_symbol();
7173 Handle<Object> reference_error =
7174 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007175 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007176 }
7177
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007178 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007179 // 'eval' is not bound in the global context. Just call the function
7180 // with the given arguments. This is not necessarily the global eval.
7181 if (receiver->IsContext()) {
7182 context = Handle<Context>::cast(receiver);
7183 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007184 } else if (receiver->IsJSContextExtensionObject()) {
7185 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007186 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007187 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007188 }
7189
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007190 // 'eval' is bound in the global context, but it may have been overwritten.
7191 // Compare it to the builtin 'GlobalEval' function to make sure.
7192 if (*callee != Top::global_context()->global_eval_fun() ||
7193 !args[1]->IsString()) {
7194 return MakePair(*callee, Top::context()->global()->global_receiver());
7195 }
7196
7197 // Deal with a normal eval call with a string argument. Compile it
7198 // and return the compiled function bound in the local context.
7199 Handle<String> source = args.at<String>(1);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007200 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007201 source,
7202 Handle<Context>(Top::context()),
7203 Top::context()->IsGlobalContext(),
7204 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007205 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7206 callee = Factory::NewFunctionFromSharedFunctionInfo(
7207 shared,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007208 Handle<Context>(Top::context()),
7209 NOT_TENURED);
7210 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007211}
7212
7213
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007214static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7215 // This utility adjusts the property attributes for newly created Function
7216 // object ("new Function(...)") by changing the map.
7217 // All it does is changing the prototype property to enumerable
7218 // as specified in ECMA262, 15.3.5.2.
7219 HandleScope scope;
7220 ASSERT(args.length() == 1);
7221 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7222 ASSERT(func->map()->instance_type() ==
7223 Top::function_instance_map()->instance_type());
7224 ASSERT(func->map()->instance_size() ==
7225 Top::function_instance_map()->instance_size());
7226 func->set_map(*Top::function_instance_map());
7227 return *func;
7228}
7229
7230
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007231// Push an array unto an array of arrays if it is not already in the
7232// array. Returns true if the element was pushed on the stack and
7233// false otherwise.
7234static Object* Runtime_PushIfAbsent(Arguments args) {
7235 ASSERT(args.length() == 2);
7236 CONVERT_CHECKED(JSArray, array, args[0]);
7237 CONVERT_CHECKED(JSArray, element, args[1]);
7238 RUNTIME_ASSERT(array->HasFastElements());
7239 int length = Smi::cast(array->length())->value();
7240 FixedArray* elements = FixedArray::cast(array->elements());
7241 for (int i = 0; i < length; i++) {
7242 if (elements->get(i) == element) return Heap::false_value();
7243 }
7244 Object* obj = array->SetFastElement(length, element);
7245 if (obj->IsFailure()) return obj;
7246 return Heap::true_value();
7247}
7248
7249
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007250/**
7251 * A simple visitor visits every element of Array's.
7252 * The backend storage can be a fixed array for fast elements case,
7253 * or a dictionary for sparse array. Since Dictionary is a subtype
7254 * of FixedArray, the class can be used by both fast and slow cases.
7255 * The second parameter of the constructor, fast_elements, specifies
7256 * whether the storage is a FixedArray or Dictionary.
7257 *
7258 * An index limit is used to deal with the situation that a result array
7259 * length overflows 32-bit non-negative integer.
7260 */
7261class ArrayConcatVisitor {
7262 public:
7263 ArrayConcatVisitor(Handle<FixedArray> storage,
7264 uint32_t index_limit,
7265 bool fast_elements) :
7266 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007267 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007268
7269 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007270 if (i >= index_limit_ - index_offset_) return;
7271 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007272
7273 if (fast_elements_) {
7274 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7275 storage_->set(index, *elm);
7276
7277 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007278 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7279 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007280 Factory::DictionaryAtNumberPut(dict, index, elm);
7281 if (!result.is_identical_to(dict))
7282 storage_ = result;
7283 }
7284 }
7285
7286 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007287 if (index_limit_ - index_offset_ < delta) {
7288 index_offset_ = index_limit_;
7289 } else {
7290 index_offset_ += delta;
7291 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007292 }
7293
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007294 Handle<FixedArray> storage() { return storage_; }
7295
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007296 private:
7297 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007298 // Limit on the accepted indices. Elements with indices larger than the
7299 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007300 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007301 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007302 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007303 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007304};
7305
7306
ager@chromium.org3811b432009-10-28 14:53:37 +00007307template<class ExternalArrayClass, class ElementType>
7308static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7309 bool elements_are_ints,
7310 bool elements_are_guaranteed_smis,
7311 uint32_t range,
7312 ArrayConcatVisitor* visitor) {
7313 Handle<ExternalArrayClass> array(
7314 ExternalArrayClass::cast(receiver->elements()));
7315 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7316
7317 if (visitor != NULL) {
7318 if (elements_are_ints) {
7319 if (elements_are_guaranteed_smis) {
7320 for (uint32_t j = 0; j < len; j++) {
7321 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7322 visitor->visit(j, e);
7323 }
7324 } else {
7325 for (uint32_t j = 0; j < len; j++) {
7326 int64_t val = static_cast<int64_t>(array->get(j));
7327 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7328 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7329 visitor->visit(j, e);
7330 } else {
7331 Handle<Object> e(
7332 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7333 visitor->visit(j, e);
7334 }
7335 }
7336 }
7337 } else {
7338 for (uint32_t j = 0; j < len; j++) {
7339 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7340 visitor->visit(j, e);
7341 }
7342 }
7343 }
7344
7345 return len;
7346}
7347
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007348/**
7349 * A helper function that visits elements of a JSObject. Only elements
7350 * whose index between 0 and range (exclusive) are visited.
7351 *
7352 * If the third parameter, visitor, is not NULL, the visitor is called
7353 * with parameters, 'visitor_index_offset + element index' and the element.
7354 *
7355 * It returns the number of visisted elements.
7356 */
7357static uint32_t IterateElements(Handle<JSObject> receiver,
7358 uint32_t range,
7359 ArrayConcatVisitor* visitor) {
7360 uint32_t num_of_elements = 0;
7361
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007362 switch (receiver->GetElementsKind()) {
7363 case JSObject::FAST_ELEMENTS: {
7364 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7365 uint32_t len = elements->length();
7366 if (range < len) {
7367 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007368 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007369
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007370 for (uint32_t j = 0; j < len; j++) {
7371 Handle<Object> e(elements->get(j));
7372 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007373 num_of_elements++;
7374 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007375 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007376 }
7377 }
7378 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007379 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007380 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007381 case JSObject::PIXEL_ELEMENTS: {
7382 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7383 uint32_t len = pixels->length();
7384 if (range < len) {
7385 len = range;
7386 }
7387
7388 for (uint32_t j = 0; j < len; j++) {
7389 num_of_elements++;
7390 if (visitor != NULL) {
7391 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7392 visitor->visit(j, e);
7393 }
7394 }
7395 break;
7396 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007397 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7398 num_of_elements =
7399 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7400 receiver, true, true, range, visitor);
7401 break;
7402 }
7403 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7404 num_of_elements =
7405 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7406 receiver, true, true, range, visitor);
7407 break;
7408 }
7409 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7410 num_of_elements =
7411 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7412 receiver, true, true, range, visitor);
7413 break;
7414 }
7415 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7416 num_of_elements =
7417 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7418 receiver, true, true, range, visitor);
7419 break;
7420 }
7421 case JSObject::EXTERNAL_INT_ELEMENTS: {
7422 num_of_elements =
7423 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7424 receiver, true, false, range, visitor);
7425 break;
7426 }
7427 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7428 num_of_elements =
7429 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7430 receiver, true, false, range, visitor);
7431 break;
7432 }
7433 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7434 num_of_elements =
7435 IterateExternalArrayElements<ExternalFloatArray, float>(
7436 receiver, false, false, range, visitor);
7437 break;
7438 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007439 case JSObject::DICTIONARY_ELEMENTS: {
7440 Handle<NumberDictionary> dict(receiver->element_dictionary());
7441 uint32_t capacity = dict->Capacity();
7442 for (uint32_t j = 0; j < capacity; j++) {
7443 Handle<Object> k(dict->KeyAt(j));
7444 if (dict->IsKey(*k)) {
7445 ASSERT(k->IsNumber());
7446 uint32_t index = static_cast<uint32_t>(k->Number());
7447 if (index < range) {
7448 num_of_elements++;
7449 if (visitor) {
7450 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7451 }
7452 }
7453 }
7454 }
7455 break;
7456 }
7457 default:
7458 UNREACHABLE();
7459 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007460 }
7461
7462 return num_of_elements;
7463}
7464
7465
7466/**
7467 * A helper function that visits elements of an Array object, and elements
7468 * on its prototypes.
7469 *
7470 * Elements on prototypes are visited first, and only elements whose indices
7471 * less than Array length are visited.
7472 *
7473 * If a ArrayConcatVisitor object is given, the visitor is called with
7474 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007475 *
7476 * The returned number of elements is an upper bound on the actual number
7477 * of elements added. If the same element occurs in more than one object
7478 * in the array's prototype chain, it will be counted more than once, but
7479 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007480 */
7481static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7482 ArrayConcatVisitor* visitor) {
7483 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7484 Handle<Object> obj = array;
7485
7486 static const int kEstimatedPrototypes = 3;
7487 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7488
7489 // Visit prototype first. If an element on the prototype is shadowed by
7490 // the inheritor using the same index, the ArrayConcatVisitor visits
7491 // the prototype element before the shadowing element.
7492 // The visitor can simply overwrite the old value by new value using
7493 // the same index. This follows Array::concat semantics.
7494 while (!obj->IsNull()) {
7495 objects.Add(Handle<JSObject>::cast(obj));
7496 obj = Handle<Object>(obj->GetPrototype());
7497 }
7498
7499 uint32_t nof_elements = 0;
7500 for (int i = objects.length() - 1; i >= 0; i--) {
7501 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007502 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007503 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007504
7505 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7506 nof_elements = JSObject::kMaxElementCount;
7507 } else {
7508 nof_elements += encountered_elements;
7509 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007510 }
7511
7512 return nof_elements;
7513}
7514
7515
7516/**
7517 * A helper function of Runtime_ArrayConcat.
7518 *
7519 * The first argument is an Array of arrays and objects. It is the
7520 * same as the arguments array of Array::concat JS function.
7521 *
7522 * If an argument is an Array object, the function visits array
7523 * elements. If an argument is not an Array object, the function
7524 * visits the object as if it is an one-element array.
7525 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007526 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007527 * non-negative number is used as new length. For example, if one
7528 * array length is 2^32 - 1, second array length is 1, the
7529 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007530 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7531 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007532 */
7533static uint32_t IterateArguments(Handle<JSArray> arguments,
7534 ArrayConcatVisitor* visitor) {
7535 uint32_t visited_elements = 0;
7536 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7537
7538 for (uint32_t i = 0; i < num_of_args; i++) {
7539 Handle<Object> obj(arguments->GetElement(i));
7540 if (obj->IsJSArray()) {
7541 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7542 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7543 uint32_t nof_elements =
7544 IterateArrayAndPrototypeElements(array, visitor);
7545 // Total elements of array and its prototype chain can be more than
7546 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007547 // the array length number of elements. We use the length as an estimate
7548 // for the actual number of elements added.
7549 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7550 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7551 visited_elements = JSArray::kMaxElementCount;
7552 } else {
7553 visited_elements += added_elements;
7554 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007555 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007556 } else {
7557 if (visitor) {
7558 visitor->visit(0, obj);
7559 visitor->increase_index_offset(1);
7560 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007561 if (visited_elements < JSArray::kMaxElementCount) {
7562 visited_elements++;
7563 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007564 }
7565 }
7566 return visited_elements;
7567}
7568
7569
7570/**
7571 * Array::concat implementation.
7572 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007573 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7574 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007575 */
7576static Object* Runtime_ArrayConcat(Arguments args) {
7577 ASSERT(args.length() == 1);
7578 HandleScope handle_scope;
7579
7580 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7581 Handle<JSArray> arguments(arg_arrays);
7582
7583 // Pass 1: estimate the number of elements of the result
7584 // (it could be more than real numbers if prototype has elements).
7585 uint32_t result_length = 0;
7586 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7587
7588 { AssertNoAllocation nogc;
7589 for (uint32_t i = 0; i < num_of_args; i++) {
7590 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007591 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007592 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007593 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007594 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7595 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007596 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007597 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007598 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7599 result_length = JSObject::kMaxElementCount;
7600 break;
7601 }
7602 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007603 }
7604 }
7605
7606 // Allocate an empty array, will set length and content later.
7607 Handle<JSArray> result = Factory::NewJSArray(0);
7608
7609 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7610 // If estimated number of elements is more than half of length, a
7611 // fixed array (fast case) is more time and space-efficient than a
7612 // dictionary.
7613 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7614
7615 Handle<FixedArray> storage;
7616 if (fast_case) {
7617 // The backing storage array must have non-existing elements to
7618 // preserve holes across concat operations.
7619 storage = Factory::NewFixedArrayWithHoles(result_length);
7620
7621 } else {
7622 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7623 uint32_t at_least_space_for = estimate_nof_elements +
7624 (estimate_nof_elements >> 2);
7625 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007626 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007627 }
7628
7629 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7630
7631 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7632
7633 IterateArguments(arguments, &visitor);
7634
7635 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007636 // Please note the storage might have changed in the visitor.
7637 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007638
7639 return *result;
7640}
7641
7642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007643// This will not allocate (flatten the string), but it may run
7644// very slowly for very deeply nested ConsStrings. For debugging use only.
7645static Object* Runtime_GlobalPrint(Arguments args) {
7646 NoHandleAllocation ha;
7647 ASSERT(args.length() == 1);
7648
7649 CONVERT_CHECKED(String, string, args[0]);
7650 StringInputBuffer buffer(string);
7651 while (buffer.has_more()) {
7652 uint16_t character = buffer.GetNext();
7653 PrintF("%c", character);
7654 }
7655 return string;
7656}
7657
ager@chromium.org5ec48922009-05-05 07:25:34 +00007658// Moves all own elements of an object, that are below a limit, to positions
7659// starting at zero. All undefined values are placed after non-undefined values,
7660// and are followed by non-existing element. Does not change the length
7661// property.
7662// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007663static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007664 ASSERT(args.length() == 2);
7665 CONVERT_CHECKED(JSObject, object, args[0]);
7666 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7667 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007668}
7669
7670
7671// Move contents of argument 0 (an array) to argument 1 (an array)
7672static Object* Runtime_MoveArrayContents(Arguments args) {
7673 ASSERT(args.length() == 2);
7674 CONVERT_CHECKED(JSArray, from, args[0]);
7675 CONVERT_CHECKED(JSArray, to, args[1]);
7676 to->SetContent(FixedArray::cast(from->elements()));
7677 to->set_length(from->length());
7678 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007679 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007680 return to;
7681}
7682
7683
7684// How many elements does this array have?
7685static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7686 ASSERT(args.length() == 1);
7687 CONVERT_CHECKED(JSArray, array, args[0]);
7688 HeapObject* elements = array->elements();
7689 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007690 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007691 } else {
7692 return array->length();
7693 }
7694}
7695
7696
7697// Returns an array that tells you where in the [0, length) interval an array
7698// might have elements. Can either return keys or intervals. Keys can have
7699// gaps in (undefined). Intervals can also span over some undefined keys.
7700static Object* Runtime_GetArrayKeys(Arguments args) {
7701 ASSERT(args.length() == 2);
7702 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007703 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007704 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007705 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007706 // Create an array and get all the keys into it, then remove all the
7707 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007708 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007709 int keys_length = keys->length();
7710 for (int i = 0; i < keys_length; i++) {
7711 Object* key = keys->get(i);
7712 uint32_t index;
7713 if (!Array::IndexFromObject(key, &index) || index >= length) {
7714 // Zap invalid keys.
7715 keys->set_undefined(i);
7716 }
7717 }
7718 return *Factory::NewJSArrayWithElements(keys);
7719 } else {
7720 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7721 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007722 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00007723 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
7724 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007725 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007726 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007727 single_interval->set(1, *length_object);
7728 return *Factory::NewJSArrayWithElements(single_interval);
7729 }
7730}
7731
7732
7733// DefineAccessor takes an optional final argument which is the
7734// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7735// to the way accessors are implemented, it is set for both the getter
7736// and setter on the first call to DefineAccessor and ignored on
7737// subsequent calls.
7738static Object* Runtime_DefineAccessor(Arguments args) {
7739 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7740 // Compute attributes.
7741 PropertyAttributes attributes = NONE;
7742 if (args.length() == 5) {
7743 CONVERT_CHECKED(Smi, attrs, args[4]);
7744 int value = attrs->value();
7745 // Only attribute bits should be set.
7746 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7747 attributes = static_cast<PropertyAttributes>(value);
7748 }
7749
7750 CONVERT_CHECKED(JSObject, obj, args[0]);
7751 CONVERT_CHECKED(String, name, args[1]);
7752 CONVERT_CHECKED(Smi, flag, args[2]);
7753 CONVERT_CHECKED(JSFunction, fun, args[3]);
7754 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7755}
7756
7757
7758static Object* Runtime_LookupAccessor(Arguments args) {
7759 ASSERT(args.length() == 3);
7760 CONVERT_CHECKED(JSObject, obj, args[0]);
7761 CONVERT_CHECKED(String, name, args[1]);
7762 CONVERT_CHECKED(Smi, flag, args[2]);
7763 return obj->LookupAccessor(name, flag->value() == 0);
7764}
7765
7766
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007767#ifdef ENABLE_DEBUGGER_SUPPORT
7768static Object* Runtime_DebugBreak(Arguments args) {
7769 ASSERT(args.length() == 0);
7770 return Execution::DebugBreakHelper();
7771}
7772
7773
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007774// Helper functions for wrapping and unwrapping stack frame ids.
7775static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007776 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007777 return Smi::FromInt(id >> 2);
7778}
7779
7780
7781static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7782 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7783}
7784
7785
7786// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007787// args[0]: debug event listener function to set or null or undefined for
7788// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007789// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007790static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007791 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007792 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7793 args[0]->IsUndefined() ||
7794 args[0]->IsNull());
7795 Handle<Object> callback = args.at<Object>(0);
7796 Handle<Object> data = args.at<Object>(1);
7797 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007798
7799 return Heap::undefined_value();
7800}
7801
7802
7803static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007804 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007805 StackGuard::DebugBreak();
7806 return Heap::undefined_value();
7807}
7808
7809
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007810static Object* DebugLookupResultValue(Object* receiver, String* name,
7811 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007812 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007813 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007814 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007815 case NORMAL:
7816 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007817 if (value->IsTheHole()) {
7818 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007819 }
7820 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007821 case FIELD:
7822 value =
7823 JSObject::cast(
7824 result->holder())->FastPropertyAt(result->GetFieldIndex());
7825 if (value->IsTheHole()) {
7826 return Heap::undefined_value();
7827 }
7828 return value;
7829 case CONSTANT_FUNCTION:
7830 return result->GetConstantFunction();
7831 case CALLBACKS: {
7832 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007833 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007834 value = receiver->GetPropertyWithCallback(
7835 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007836 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007837 value = Top::pending_exception();
7838 Top::clear_pending_exception();
7839 if (caught_exception != NULL) {
7840 *caught_exception = true;
7841 }
7842 }
7843 return value;
7844 } else {
7845 return Heap::undefined_value();
7846 }
7847 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007848 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007849 case MAP_TRANSITION:
7850 case CONSTANT_TRANSITION:
7851 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007852 return Heap::undefined_value();
7853 default:
7854 UNREACHABLE();
7855 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007856 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007857 return Heap::undefined_value();
7858}
7859
7860
ager@chromium.org32912102009-01-16 10:38:43 +00007861// Get debugger related details for an object property.
7862// args[0]: object holding property
7863// args[1]: name of the property
7864//
7865// The array returned contains the following information:
7866// 0: Property value
7867// 1: Property details
7868// 2: Property value is exception
7869// 3: Getter function if defined
7870// 4: Setter function if defined
7871// Items 2-4 are only filled if the property has either a getter or a setter
7872// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007873static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007874 HandleScope scope;
7875
7876 ASSERT(args.length() == 2);
7877
7878 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7879 CONVERT_ARG_CHECKED(String, name, 1);
7880
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007881 // Make sure to set the current context to the context before the debugger was
7882 // entered (if the debugger is entered). The reason for switching context here
7883 // is that for some property lookups (accessors and interceptors) callbacks
7884 // into the embedding application can occour, and the embedding application
7885 // could have the assumption that its own global context is the current
7886 // context and not some internal debugger context.
7887 SaveContext save;
7888 if (Debug::InDebugger()) {
7889 Top::set_context(*Debug::debugger_entry()->GetContext());
7890 }
7891
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007892 // Skip the global proxy as it has no properties and always delegates to the
7893 // real global object.
7894 if (obj->IsJSGlobalProxy()) {
7895 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
7896 }
7897
7898
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007899 // Check if the name is trivially convertible to an index and get the element
7900 // if so.
7901 uint32_t index;
7902 if (name->AsArrayIndex(&index)) {
7903 Handle<FixedArray> details = Factory::NewFixedArray(2);
7904 details->set(0, Runtime::GetElementOrCharAt(obj, index));
7905 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
7906 return *Factory::NewJSArrayWithElements(details);
7907 }
7908
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007909 // Find the number of objects making up this.
7910 int length = LocalPrototypeChainLength(*obj);
7911
7912 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007913 Handle<JSObject> jsproto = obj;
7914 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007915 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007916 jsproto->LocalLookup(*name, &result);
7917 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007918 // LookupResult is not GC safe as it holds raw object pointers.
7919 // GC can happen later in this code so put the required fields into
7920 // local variables using handles when required for later use.
7921 PropertyType result_type = result.type();
7922 Handle<Object> result_callback_obj;
7923 if (result_type == CALLBACKS) {
7924 result_callback_obj = Handle<Object>(result.GetCallbackObject());
7925 }
7926 Smi* property_details = result.GetPropertyDetails().AsSmi();
7927 // DebugLookupResultValue can cause GC so details from LookupResult needs
7928 // to be copied to handles before this.
7929 bool caught_exception = false;
7930 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
7931 &caught_exception);
7932 if (raw_value->IsFailure()) return raw_value;
7933 Handle<Object> value(raw_value);
7934
7935 // If the callback object is a fixed array then it contains JavaScript
7936 // getter and/or setter.
7937 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
7938 result_callback_obj->IsFixedArray();
7939 Handle<FixedArray> details =
7940 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
7941 details->set(0, *value);
7942 details->set(1, property_details);
7943 if (hasJavaScriptAccessors) {
7944 details->set(2,
7945 caught_exception ? Heap::true_value()
7946 : Heap::false_value());
7947 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
7948 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
7949 }
7950
7951 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007952 }
7953 if (i < length - 1) {
7954 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
7955 }
7956 }
7957
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007958 return Heap::undefined_value();
7959}
7960
7961
7962static Object* Runtime_DebugGetProperty(Arguments args) {
7963 HandleScope scope;
7964
7965 ASSERT(args.length() == 2);
7966
7967 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7968 CONVERT_ARG_CHECKED(String, name, 1);
7969
7970 LookupResult result;
7971 obj->Lookup(*name, &result);
7972 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007973 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007974 }
7975 return Heap::undefined_value();
7976}
7977
7978
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007979// Return the property type calculated from the property details.
7980// args[0]: smi with property details.
7981static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
7982 ASSERT(args.length() == 1);
7983 CONVERT_CHECKED(Smi, details, args[0]);
7984 PropertyType type = PropertyDetails(details).type();
7985 return Smi::FromInt(static_cast<int>(type));
7986}
7987
7988
7989// Return the property attribute calculated from the property details.
7990// args[0]: smi with property details.
7991static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
7992 ASSERT(args.length() == 1);
7993 CONVERT_CHECKED(Smi, details, args[0]);
7994 PropertyAttributes attributes = PropertyDetails(details).attributes();
7995 return Smi::FromInt(static_cast<int>(attributes));
7996}
7997
7998
7999// Return the property insertion index calculated from the property details.
8000// args[0]: smi with property details.
8001static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8002 ASSERT(args.length() == 1);
8003 CONVERT_CHECKED(Smi, details, args[0]);
8004 int index = PropertyDetails(details).index();
8005 return Smi::FromInt(index);
8006}
8007
8008
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008009// Return property value from named interceptor.
8010// args[0]: object
8011// args[1]: property name
8012static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8013 HandleScope scope;
8014 ASSERT(args.length() == 2);
8015 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8016 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8017 CONVERT_ARG_CHECKED(String, name, 1);
8018
8019 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008020 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008021}
8022
8023
8024// Return element value from indexed interceptor.
8025// args[0]: object
8026// args[1]: index
8027static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8028 HandleScope scope;
8029 ASSERT(args.length() == 2);
8030 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8031 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8032 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8033
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008034 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008035}
8036
8037
8038static Object* Runtime_CheckExecutionState(Arguments args) {
8039 ASSERT(args.length() >= 1);
8040 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008041 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008042 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008043 return Top::Throw(Heap::illegal_execution_state_symbol());
8044 }
8045
8046 return Heap::true_value();
8047}
8048
8049
8050static Object* Runtime_GetFrameCount(Arguments args) {
8051 HandleScope scope;
8052 ASSERT(args.length() == 1);
8053
8054 // Check arguments.
8055 Object* result = Runtime_CheckExecutionState(args);
8056 if (result->IsFailure()) return result;
8057
8058 // Count all frames which are relevant to debugging stack trace.
8059 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008060 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008061 if (id == StackFrame::NO_ID) {
8062 // If there is no JavaScript stack frame count is 0.
8063 return Smi::FromInt(0);
8064 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008065 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8066 return Smi::FromInt(n);
8067}
8068
8069
8070static const int kFrameDetailsFrameIdIndex = 0;
8071static const int kFrameDetailsReceiverIndex = 1;
8072static const int kFrameDetailsFunctionIndex = 2;
8073static const int kFrameDetailsArgumentCountIndex = 3;
8074static const int kFrameDetailsLocalCountIndex = 4;
8075static const int kFrameDetailsSourcePositionIndex = 5;
8076static const int kFrameDetailsConstructCallIndex = 6;
8077static const int kFrameDetailsDebuggerFrameIndex = 7;
8078static const int kFrameDetailsFirstDynamicIndex = 8;
8079
8080// Return an array with frame details
8081// args[0]: number: break id
8082// args[1]: number: frame index
8083//
8084// The array returned contains the following information:
8085// 0: Frame id
8086// 1: Receiver
8087// 2: Function
8088// 3: Argument count
8089// 4: Local count
8090// 5: Source position
8091// 6: Constructor call
8092// 7: Debugger frame
8093// Arguments name, value
8094// Locals name, value
8095static Object* Runtime_GetFrameDetails(Arguments args) {
8096 HandleScope scope;
8097 ASSERT(args.length() == 2);
8098
8099 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008100 Object* check = Runtime_CheckExecutionState(args);
8101 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008102 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8103
8104 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008105 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008106 if (id == StackFrame::NO_ID) {
8107 // If there are no JavaScript stack frames return undefined.
8108 return Heap::undefined_value();
8109 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008110 int count = 0;
8111 JavaScriptFrameIterator it(id);
8112 for (; !it.done(); it.Advance()) {
8113 if (count == index) break;
8114 count++;
8115 }
8116 if (it.done()) return Heap::undefined_value();
8117
8118 // Traverse the saved contexts chain to find the active context for the
8119 // selected frame.
8120 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008121 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008122 save = save->prev();
8123 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008124 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008125
8126 // Get the frame id.
8127 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8128
8129 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008130 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008131
8132 // Check for constructor frame.
8133 bool constructor = it.frame()->IsConstructor();
8134
8135 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008136 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008137 ScopeInfo<> info(*code);
8138
8139 // Get the context.
8140 Handle<Context> context(Context::cast(it.frame()->context()));
8141
8142 // Get the locals names and values into a temporary array.
8143 //
8144 // TODO(1240907): Hide compiler-introduced stack variables
8145 // (e.g. .result)? For users of the debugger, they will probably be
8146 // confusing.
8147 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8148 for (int i = 0; i < info.NumberOfLocals(); i++) {
8149 // Name of the local.
8150 locals->set(i * 2, *info.LocalName(i));
8151
8152 // Fetch the value of the local - either from the stack or from a
8153 // heap-allocated context.
8154 if (i < info.number_of_stack_slots()) {
8155 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8156 } else {
8157 Handle<String> name = info.LocalName(i);
8158 // Traverse the context chain to the function context as all local
8159 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008160 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008161 context = Handle<Context>(context->previous());
8162 }
8163 ASSERT(context->is_function_context());
8164 locals->set(i * 2 + 1,
8165 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8166 NULL)));
8167 }
8168 }
8169
8170 // Now advance to the arguments adapter frame (if any). If contains all
8171 // the provided parameters and
8172
8173 // Now advance to the arguments adapter frame (if any). It contains all
8174 // the provided parameters whereas the function frame always have the number
8175 // of arguments matching the functions parameters. The rest of the
8176 // information (except for what is collected above) is the same.
8177 it.AdvanceToArgumentsFrame();
8178
8179 // Find the number of arguments to fill. At least fill the number of
8180 // parameters for the function and fill more if more parameters are provided.
8181 int argument_count = info.number_of_parameters();
8182 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8183 argument_count = it.frame()->GetProvidedParametersCount();
8184 }
8185
8186 // Calculate the size of the result.
8187 int details_size = kFrameDetailsFirstDynamicIndex +
8188 2 * (argument_count + info.NumberOfLocals());
8189 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8190
8191 // Add the frame id.
8192 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8193
8194 // Add the function (same as in function frame).
8195 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8196
8197 // Add the arguments count.
8198 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8199
8200 // Add the locals count
8201 details->set(kFrameDetailsLocalCountIndex,
8202 Smi::FromInt(info.NumberOfLocals()));
8203
8204 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008205 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008206 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8207 } else {
8208 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8209 }
8210
8211 // Add the constructor information.
8212 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8213
8214 // Add information on whether this frame is invoked in the debugger context.
8215 details->set(kFrameDetailsDebuggerFrameIndex,
8216 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8217
8218 // Fill the dynamic part.
8219 int details_index = kFrameDetailsFirstDynamicIndex;
8220
8221 // Add arguments name and value.
8222 for (int i = 0; i < argument_count; i++) {
8223 // Name of the argument.
8224 if (i < info.number_of_parameters()) {
8225 details->set(details_index++, *info.parameter_name(i));
8226 } else {
8227 details->set(details_index++, Heap::undefined_value());
8228 }
8229
8230 // Parameter value.
8231 if (i < it.frame()->GetProvidedParametersCount()) {
8232 details->set(details_index++, it.frame()->GetParameter(i));
8233 } else {
8234 details->set(details_index++, Heap::undefined_value());
8235 }
8236 }
8237
8238 // Add locals name and value from the temporary copy from the function frame.
8239 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8240 details->set(details_index++, locals->get(i));
8241 }
8242
8243 // Add the receiver (same as in function frame).
8244 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8245 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8246 Handle<Object> receiver(it.frame()->receiver());
8247 if (!receiver->IsJSObject()) {
8248 // If the receiver is NOT a JSObject we have hit an optimization
8249 // where a value object is not converted into a wrapped JS objects.
8250 // To hide this optimization from the debugger, we wrap the receiver
8251 // by creating correct wrapper object based on the calling frame's
8252 // global context.
8253 it.Advance();
8254 Handle<Context> calling_frames_global_context(
8255 Context::cast(Context::cast(it.frame()->context())->global_context()));
8256 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8257 }
8258 details->set(kFrameDetailsReceiverIndex, *receiver);
8259
8260 ASSERT_EQ(details_size, details_index);
8261 return *Factory::NewJSArrayWithElements(details);
8262}
8263
8264
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008265// Copy all the context locals into an object used to materialize a scope.
8266static void CopyContextLocalsToScopeObject(Handle<Code> code,
8267 ScopeInfo<>& scope_info,
8268 Handle<Context> context,
8269 Handle<JSObject> scope_object) {
8270 // Fill all context locals to the context extension.
8271 for (int i = Context::MIN_CONTEXT_SLOTS;
8272 i < scope_info.number_of_context_slots();
8273 i++) {
8274 int context_index =
8275 ScopeInfo<>::ContextSlotIndex(*code,
8276 *scope_info.context_slot_name(i),
8277 NULL);
8278
8279 // Don't include the arguments shadow (.arguments) context variable.
8280 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8281 SetProperty(scope_object,
8282 scope_info.context_slot_name(i),
8283 Handle<Object>(context->get(context_index)), NONE);
8284 }
8285 }
8286}
8287
8288
8289// Create a plain JSObject which materializes the local scope for the specified
8290// frame.
8291static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8292 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8293 Handle<Code> code(function->code());
8294 ScopeInfo<> scope_info(*code);
8295
8296 // Allocate and initialize a JSObject with all the arguments, stack locals
8297 // heap locals and extension properties of the debugged function.
8298 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8299
8300 // First fill all parameters.
8301 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8302 SetProperty(local_scope,
8303 scope_info.parameter_name(i),
8304 Handle<Object>(frame->GetParameter(i)), NONE);
8305 }
8306
8307 // Second fill all stack locals.
8308 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8309 SetProperty(local_scope,
8310 scope_info.stack_slot_name(i),
8311 Handle<Object>(frame->GetExpression(i)), NONE);
8312 }
8313
8314 // Third fill all context locals.
8315 Handle<Context> frame_context(Context::cast(frame->context()));
8316 Handle<Context> function_context(frame_context->fcontext());
8317 CopyContextLocalsToScopeObject(code, scope_info,
8318 function_context, local_scope);
8319
8320 // Finally copy any properties from the function context extension. This will
8321 // be variables introduced by eval.
8322 if (function_context->closure() == *function) {
8323 if (function_context->has_extension() &&
8324 !function_context->IsGlobalContext()) {
8325 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008326 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008327 for (int i = 0; i < keys->length(); i++) {
8328 // Names of variables introduced by eval are strings.
8329 ASSERT(keys->get(i)->IsString());
8330 Handle<String> key(String::cast(keys->get(i)));
8331 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8332 }
8333 }
8334 }
8335 return local_scope;
8336}
8337
8338
8339// Create a plain JSObject which materializes the closure content for the
8340// context.
8341static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8342 ASSERT(context->is_function_context());
8343
8344 Handle<Code> code(context->closure()->code());
8345 ScopeInfo<> scope_info(*code);
8346
8347 // Allocate and initialize a JSObject with all the content of theis function
8348 // closure.
8349 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8350
8351 // Check whether the arguments shadow object exists.
8352 int arguments_shadow_index =
8353 ScopeInfo<>::ContextSlotIndex(*code,
8354 Heap::arguments_shadow_symbol(),
8355 NULL);
8356 if (arguments_shadow_index >= 0) {
8357 // In this case all the arguments are available in the arguments shadow
8358 // object.
8359 Handle<JSObject> arguments_shadow(
8360 JSObject::cast(context->get(arguments_shadow_index)));
8361 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8362 SetProperty(closure_scope,
8363 scope_info.parameter_name(i),
8364 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8365 }
8366 }
8367
8368 // Fill all context locals to the context extension.
8369 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8370
8371 // Finally copy any properties from the function context extension. This will
8372 // be variables introduced by eval.
8373 if (context->has_extension()) {
8374 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008375 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008376 for (int i = 0; i < keys->length(); i++) {
8377 // Names of variables introduced by eval are strings.
8378 ASSERT(keys->get(i)->IsString());
8379 Handle<String> key(String::cast(keys->get(i)));
8380 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8381 }
8382 }
8383
8384 return closure_scope;
8385}
8386
8387
8388// Iterate over the actual scopes visible from a stack frame. All scopes are
8389// backed by an actual context except the local scope, which is inserted
8390// "artifically" in the context chain.
8391class ScopeIterator {
8392 public:
8393 enum ScopeType {
8394 ScopeTypeGlobal = 0,
8395 ScopeTypeLocal,
8396 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008397 ScopeTypeClosure,
8398 // Every catch block contains an implicit with block (its parameter is
8399 // a JSContextExtensionObject) that extends current scope with a variable
8400 // holding exception object. Such with blocks are treated as scopes of their
8401 // own type.
8402 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008403 };
8404
8405 explicit ScopeIterator(JavaScriptFrame* frame)
8406 : frame_(frame),
8407 function_(JSFunction::cast(frame->function())),
8408 context_(Context::cast(frame->context())),
8409 local_done_(false),
8410 at_local_(false) {
8411
8412 // Check whether the first scope is actually a local scope.
8413 if (context_->IsGlobalContext()) {
8414 // If there is a stack slot for .result then this local scope has been
8415 // created for evaluating top level code and it is not a real local scope.
8416 // Checking for the existence of .result seems fragile, but the scope info
8417 // saved with the code object does not otherwise have that information.
8418 Handle<Code> code(function_->code());
8419 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8420 at_local_ = index < 0;
8421 } else if (context_->is_function_context()) {
8422 at_local_ = true;
8423 }
8424 }
8425
8426 // More scopes?
8427 bool Done() { return context_.is_null(); }
8428
8429 // Move to the next scope.
8430 void Next() {
8431 // If at a local scope mark the local scope as passed.
8432 if (at_local_) {
8433 at_local_ = false;
8434 local_done_ = true;
8435
8436 // If the current context is not associated with the local scope the
8437 // current context is the next real scope, so don't move to the next
8438 // context in this case.
8439 if (context_->closure() != *function_) {
8440 return;
8441 }
8442 }
8443
8444 // The global scope is always the last in the chain.
8445 if (context_->IsGlobalContext()) {
8446 context_ = Handle<Context>();
8447 return;
8448 }
8449
8450 // Move to the next context.
8451 if (context_->is_function_context()) {
8452 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8453 } else {
8454 context_ = Handle<Context>(context_->previous());
8455 }
8456
8457 // If passing the local scope indicate that the current scope is now the
8458 // local scope.
8459 if (!local_done_ &&
8460 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8461 at_local_ = true;
8462 }
8463 }
8464
8465 // Return the type of the current scope.
8466 int Type() {
8467 if (at_local_) {
8468 return ScopeTypeLocal;
8469 }
8470 if (context_->IsGlobalContext()) {
8471 ASSERT(context_->global()->IsGlobalObject());
8472 return ScopeTypeGlobal;
8473 }
8474 if (context_->is_function_context()) {
8475 return ScopeTypeClosure;
8476 }
8477 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008478 // Current scope is either an explicit with statement or a with statement
8479 // implicitely generated for a catch block.
8480 // If the extension object here is a JSContextExtensionObject then
8481 // current with statement is one frome a catch block otherwise it's a
8482 // regular with statement.
8483 if (context_->extension()->IsJSContextExtensionObject()) {
8484 return ScopeTypeCatch;
8485 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008486 return ScopeTypeWith;
8487 }
8488
8489 // Return the JavaScript object with the content of the current scope.
8490 Handle<JSObject> ScopeObject() {
8491 switch (Type()) {
8492 case ScopeIterator::ScopeTypeGlobal:
8493 return Handle<JSObject>(CurrentContext()->global());
8494 break;
8495 case ScopeIterator::ScopeTypeLocal:
8496 // Materialize the content of the local scope into a JSObject.
8497 return MaterializeLocalScope(frame_);
8498 break;
8499 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008500 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008501 // Return the with object.
8502 return Handle<JSObject>(CurrentContext()->extension());
8503 break;
8504 case ScopeIterator::ScopeTypeClosure:
8505 // Materialize the content of the closure scope into a JSObject.
8506 return MaterializeClosure(CurrentContext());
8507 break;
8508 }
8509 UNREACHABLE();
8510 return Handle<JSObject>();
8511 }
8512
8513 // Return the context for this scope. For the local context there might not
8514 // be an actual context.
8515 Handle<Context> CurrentContext() {
8516 if (at_local_ && context_->closure() != *function_) {
8517 return Handle<Context>();
8518 }
8519 return context_;
8520 }
8521
8522#ifdef DEBUG
8523 // Debug print of the content of the current scope.
8524 void DebugPrint() {
8525 switch (Type()) {
8526 case ScopeIterator::ScopeTypeGlobal:
8527 PrintF("Global:\n");
8528 CurrentContext()->Print();
8529 break;
8530
8531 case ScopeIterator::ScopeTypeLocal: {
8532 PrintF("Local:\n");
8533 Handle<Code> code(function_->code());
8534 ScopeInfo<> scope_info(*code);
8535 scope_info.Print();
8536 if (!CurrentContext().is_null()) {
8537 CurrentContext()->Print();
8538 if (CurrentContext()->has_extension()) {
8539 Handle<JSObject> extension =
8540 Handle<JSObject>(CurrentContext()->extension());
8541 if (extension->IsJSContextExtensionObject()) {
8542 extension->Print();
8543 }
8544 }
8545 }
8546 break;
8547 }
8548
8549 case ScopeIterator::ScopeTypeWith: {
8550 PrintF("With:\n");
8551 Handle<JSObject> extension =
8552 Handle<JSObject>(CurrentContext()->extension());
8553 extension->Print();
8554 break;
8555 }
8556
ager@chromium.orga1645e22009-09-09 19:27:10 +00008557 case ScopeIterator::ScopeTypeCatch: {
8558 PrintF("Catch:\n");
8559 Handle<JSObject> extension =
8560 Handle<JSObject>(CurrentContext()->extension());
8561 extension->Print();
8562 break;
8563 }
8564
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008565 case ScopeIterator::ScopeTypeClosure: {
8566 PrintF("Closure:\n");
8567 CurrentContext()->Print();
8568 if (CurrentContext()->has_extension()) {
8569 Handle<JSObject> extension =
8570 Handle<JSObject>(CurrentContext()->extension());
8571 if (extension->IsJSContextExtensionObject()) {
8572 extension->Print();
8573 }
8574 }
8575 break;
8576 }
8577
8578 default:
8579 UNREACHABLE();
8580 }
8581 PrintF("\n");
8582 }
8583#endif
8584
8585 private:
8586 JavaScriptFrame* frame_;
8587 Handle<JSFunction> function_;
8588 Handle<Context> context_;
8589 bool local_done_;
8590 bool at_local_;
8591
8592 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8593};
8594
8595
8596static Object* Runtime_GetScopeCount(Arguments args) {
8597 HandleScope scope;
8598 ASSERT(args.length() == 2);
8599
8600 // Check arguments.
8601 Object* check = Runtime_CheckExecutionState(args);
8602 if (check->IsFailure()) return check;
8603 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8604
8605 // Get the frame where the debugging is performed.
8606 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8607 JavaScriptFrameIterator it(id);
8608 JavaScriptFrame* frame = it.frame();
8609
8610 // Count the visible scopes.
8611 int n = 0;
8612 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8613 n++;
8614 }
8615
8616 return Smi::FromInt(n);
8617}
8618
8619
8620static const int kScopeDetailsTypeIndex = 0;
8621static const int kScopeDetailsObjectIndex = 1;
8622static const int kScopeDetailsSize = 2;
8623
8624// Return an array with scope details
8625// args[0]: number: break id
8626// args[1]: number: frame index
8627// args[2]: number: scope index
8628//
8629// The array returned contains the following information:
8630// 0: Scope type
8631// 1: Scope object
8632static Object* Runtime_GetScopeDetails(Arguments args) {
8633 HandleScope scope;
8634 ASSERT(args.length() == 3);
8635
8636 // Check arguments.
8637 Object* check = Runtime_CheckExecutionState(args);
8638 if (check->IsFailure()) return check;
8639 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8640 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8641
8642 // Get the frame where the debugging is performed.
8643 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8644 JavaScriptFrameIterator frame_it(id);
8645 JavaScriptFrame* frame = frame_it.frame();
8646
8647 // Find the requested scope.
8648 int n = 0;
8649 ScopeIterator it(frame);
8650 for (; !it.Done() && n < index; it.Next()) {
8651 n++;
8652 }
8653 if (it.Done()) {
8654 return Heap::undefined_value();
8655 }
8656
8657 // Calculate the size of the result.
8658 int details_size = kScopeDetailsSize;
8659 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8660
8661 // Fill in scope details.
8662 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8663 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8664
8665 return *Factory::NewJSArrayWithElements(details);
8666}
8667
8668
8669static Object* Runtime_DebugPrintScopes(Arguments args) {
8670 HandleScope scope;
8671 ASSERT(args.length() == 0);
8672
8673#ifdef DEBUG
8674 // Print the scopes for the top frame.
8675 StackFrameLocator locator;
8676 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8677 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8678 it.DebugPrint();
8679 }
8680#endif
8681 return Heap::undefined_value();
8682}
8683
8684
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008685static Object* Runtime_GetCFrames(Arguments args) {
8686 HandleScope scope;
8687 ASSERT(args.length() == 1);
8688 Object* result = Runtime_CheckExecutionState(args);
8689 if (result->IsFailure()) return result;
8690
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008691#if V8_HOST_ARCH_64_BIT
8692 UNIMPLEMENTED();
8693 return Heap::undefined_value();
8694#else
8695
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008696 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008697 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8698 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008699 if (frames_count == OS::kStackWalkError) {
8700 return Heap::undefined_value();
8701 }
8702
8703 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8704 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8705 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8706 for (int i = 0; i < frames_count; i++) {
8707 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8708 frame_value->SetProperty(
8709 *address_str,
8710 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8711 NONE);
8712
8713 // Get the stack walk text for this frame.
8714 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008715 int frame_text_length = StrLength(frames[i].text);
8716 if (frame_text_length > 0) {
8717 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008718 frame_text = Factory::NewStringFromAscii(str);
8719 }
8720
8721 if (!frame_text.is_null()) {
8722 frame_value->SetProperty(*text_str, *frame_text, NONE);
8723 }
8724
8725 frames_array->set(i, *frame_value);
8726 }
8727 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008728#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008729}
8730
8731
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008732static Object* Runtime_GetThreadCount(Arguments args) {
8733 HandleScope scope;
8734 ASSERT(args.length() == 1);
8735
8736 // Check arguments.
8737 Object* result = Runtime_CheckExecutionState(args);
8738 if (result->IsFailure()) return result;
8739
8740 // Count all archived V8 threads.
8741 int n = 0;
8742 for (ThreadState* thread = ThreadState::FirstInUse();
8743 thread != NULL;
8744 thread = thread->Next()) {
8745 n++;
8746 }
8747
8748 // Total number of threads is current thread and archived threads.
8749 return Smi::FromInt(n + 1);
8750}
8751
8752
8753static const int kThreadDetailsCurrentThreadIndex = 0;
8754static const int kThreadDetailsThreadIdIndex = 1;
8755static const int kThreadDetailsSize = 2;
8756
8757// Return an array with thread details
8758// args[0]: number: break id
8759// args[1]: number: thread index
8760//
8761// The array returned contains the following information:
8762// 0: Is current thread?
8763// 1: Thread id
8764static Object* Runtime_GetThreadDetails(Arguments args) {
8765 HandleScope scope;
8766 ASSERT(args.length() == 2);
8767
8768 // Check arguments.
8769 Object* check = Runtime_CheckExecutionState(args);
8770 if (check->IsFailure()) return check;
8771 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8772
8773 // Allocate array for result.
8774 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8775
8776 // Thread index 0 is current thread.
8777 if (index == 0) {
8778 // Fill the details.
8779 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8780 details->set(kThreadDetailsThreadIdIndex,
8781 Smi::FromInt(ThreadManager::CurrentId()));
8782 } else {
8783 // Find the thread with the requested index.
8784 int n = 1;
8785 ThreadState* thread = ThreadState::FirstInUse();
8786 while (index != n && thread != NULL) {
8787 thread = thread->Next();
8788 n++;
8789 }
8790 if (thread == NULL) {
8791 return Heap::undefined_value();
8792 }
8793
8794 // Fill the details.
8795 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8796 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8797 }
8798
8799 // Convert to JS array and return.
8800 return *Factory::NewJSArrayWithElements(details);
8801}
8802
8803
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008804static Object* Runtime_GetBreakLocations(Arguments args) {
8805 HandleScope scope;
8806 ASSERT(args.length() == 1);
8807
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008808 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8809 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008810 // Find the number of break points
8811 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8812 if (break_locations->IsUndefined()) return Heap::undefined_value();
8813 // Return array as JS array
8814 return *Factory::NewJSArrayWithElements(
8815 Handle<FixedArray>::cast(break_locations));
8816}
8817
8818
8819// Set a break point in a function
8820// args[0]: function
8821// args[1]: number: break source position (within the function source)
8822// args[2]: number: break point object
8823static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8824 HandleScope scope;
8825 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008826 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8827 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008828 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8829 RUNTIME_ASSERT(source_position >= 0);
8830 Handle<Object> break_point_object_arg = args.at<Object>(2);
8831
8832 // Set break point.
8833 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8834
8835 return Heap::undefined_value();
8836}
8837
8838
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008839Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8840 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008841 // Iterate the heap looking for SharedFunctionInfo generated from the
8842 // script. The inner most SharedFunctionInfo containing the source position
8843 // for the requested break point is found.
8844 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8845 // which is found is not compiled it is compiled and the heap is iterated
8846 // again as the compilation might create inner functions from the newly
8847 // compiled function and the actual requested break point might be in one of
8848 // these functions.
8849 bool done = false;
8850 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008851 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008852 Handle<SharedFunctionInfo> target;
8853 // The current candidate for the last function in script:
8854 Handle<SharedFunctionInfo> last;
8855 while (!done) {
8856 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008857 for (HeapObject* obj = iterator.next();
8858 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008859 if (obj->IsSharedFunctionInfo()) {
8860 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
8861 if (shared->script() == *script) {
8862 // If the SharedFunctionInfo found has the requested script data and
8863 // contains the source position it is a candidate.
8864 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00008865 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008866 start_position = shared->start_position();
8867 }
8868 if (start_position <= position &&
8869 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00008870 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008871 // candidate this is the new candidate.
8872 if (target.is_null()) {
8873 target_start_position = start_position;
8874 target = shared;
8875 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00008876 if (target_start_position == start_position &&
8877 shared->end_position() == target->end_position()) {
8878 // If a top-level function contain only one function
8879 // declartion the source for the top-level and the function is
8880 // the same. In that case prefer the non top-level function.
8881 if (!shared->is_toplevel()) {
8882 target_start_position = start_position;
8883 target = shared;
8884 }
8885 } else if (target_start_position <= start_position &&
8886 shared->end_position() <= target->end_position()) {
8887 // This containment check includes equality as a function inside
8888 // a top-level function can share either start or end position
8889 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008890 target_start_position = start_position;
8891 target = shared;
8892 }
8893 }
8894 }
8895
8896 // Keep track of the last function in the script.
8897 if (last.is_null() ||
8898 shared->end_position() > last->start_position()) {
8899 last = shared;
8900 }
8901 }
8902 }
8903 }
8904
8905 // Make sure some candidate is selected.
8906 if (target.is_null()) {
8907 if (!last.is_null()) {
8908 // Position after the last function - use last.
8909 target = last;
8910 } else {
8911 // Unable to find function - possibly script without any function.
8912 return Heap::undefined_value();
8913 }
8914 }
8915
8916 // If the candidate found is compiled we are done. NOTE: when lazy
8917 // compilation of inner functions is introduced some additional checking
8918 // needs to be done here to compile inner functions.
8919 done = target->is_compiled();
8920 if (!done) {
8921 // If the candidate is not compiled compile it to reveal any inner
8922 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008923 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008924 }
8925 }
8926
8927 return *target;
8928}
8929
8930
8931// Change the state of a break point in a script. NOTE: Regarding performance
8932// see the NOTE for GetScriptFromScriptData.
8933// args[0]: script to set break point in
8934// args[1]: number: break source position (within the script source)
8935// args[2]: number: break point object
8936static Object* Runtime_SetScriptBreakPoint(Arguments args) {
8937 HandleScope scope;
8938 ASSERT(args.length() == 3);
8939 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
8940 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8941 RUNTIME_ASSERT(source_position >= 0);
8942 Handle<Object> break_point_object_arg = args.at<Object>(2);
8943
8944 // Get the script from the script wrapper.
8945 RUNTIME_ASSERT(wrapper->value()->IsScript());
8946 Handle<Script> script(Script::cast(wrapper->value()));
8947
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008948 Object* result = Runtime::FindSharedFunctionInfoInScript(
8949 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008950 if (!result->IsUndefined()) {
8951 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
8952 // Find position within function. The script position might be before the
8953 // source position of the first function.
8954 int position;
8955 if (shared->start_position() > source_position) {
8956 position = 0;
8957 } else {
8958 position = source_position - shared->start_position();
8959 }
8960 Debug::SetBreakPoint(shared, position, break_point_object_arg);
8961 }
8962 return Heap::undefined_value();
8963}
8964
8965
8966// Clear a break point
8967// args[0]: number: break point object
8968static Object* Runtime_ClearBreakPoint(Arguments args) {
8969 HandleScope scope;
8970 ASSERT(args.length() == 1);
8971 Handle<Object> break_point_object_arg = args.at<Object>(0);
8972
8973 // Clear break point.
8974 Debug::ClearBreakPoint(break_point_object_arg);
8975
8976 return Heap::undefined_value();
8977}
8978
8979
8980// Change the state of break on exceptions
8981// args[0]: boolean indicating uncaught exceptions
8982// args[1]: boolean indicating on/off
8983static Object* Runtime_ChangeBreakOnException(Arguments args) {
8984 HandleScope scope;
8985 ASSERT(args.length() == 2);
8986 ASSERT(args[0]->IsNumber());
8987 ASSERT(args[1]->IsBoolean());
8988
8989 // Update break point state
8990 ExceptionBreakType type =
8991 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
8992 bool enable = args[1]->ToBoolean()->IsTrue();
8993 Debug::ChangeBreakOnException(type, enable);
8994 return Heap::undefined_value();
8995}
8996
8997
8998// Prepare for stepping
8999// args[0]: break id for checking execution state
9000// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009001// args[2]: number of times to perform the step, for step out it is the number
9002// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009003static Object* Runtime_PrepareStep(Arguments args) {
9004 HandleScope scope;
9005 ASSERT(args.length() == 3);
9006 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009007 Object* check = Runtime_CheckExecutionState(args);
9008 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009009 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9010 return Top::Throw(Heap::illegal_argument_symbol());
9011 }
9012
9013 // Get the step action and check validity.
9014 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9015 if (step_action != StepIn &&
9016 step_action != StepNext &&
9017 step_action != StepOut &&
9018 step_action != StepInMin &&
9019 step_action != StepMin) {
9020 return Top::Throw(Heap::illegal_argument_symbol());
9021 }
9022
9023 // Get the number of steps.
9024 int step_count = NumberToInt32(args[2]);
9025 if (step_count < 1) {
9026 return Top::Throw(Heap::illegal_argument_symbol());
9027 }
9028
ager@chromium.orga1645e22009-09-09 19:27:10 +00009029 // Clear all current stepping setup.
9030 Debug::ClearStepping();
9031
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009032 // Prepare step.
9033 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9034 return Heap::undefined_value();
9035}
9036
9037
9038// Clear all stepping set by PrepareStep.
9039static Object* Runtime_ClearStepping(Arguments args) {
9040 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009041 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009042 Debug::ClearStepping();
9043 return Heap::undefined_value();
9044}
9045
9046
9047// Creates a copy of the with context chain. The copy of the context chain is
9048// is linked to the function context supplied.
9049static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9050 Handle<Context> function_context) {
9051 // At the bottom of the chain. Return the function context to link to.
9052 if (context_chain->is_function_context()) {
9053 return function_context;
9054 }
9055
9056 // Recursively copy the with contexts.
9057 Handle<Context> previous(context_chain->previous());
9058 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9059 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009060 CopyWithContextChain(function_context, previous),
9061 extension,
9062 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009063}
9064
9065
9066// Helper function to find or create the arguments object for
9067// Runtime_DebugEvaluate.
9068static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9069 Handle<JSFunction> function,
9070 Handle<Code> code,
9071 const ScopeInfo<>* sinfo,
9072 Handle<Context> function_context) {
9073 // Try to find the value of 'arguments' to pass as parameter. If it is not
9074 // found (that is the debugged function does not reference 'arguments' and
9075 // does not support eval) then create an 'arguments' object.
9076 int index;
9077 if (sinfo->number_of_stack_slots() > 0) {
9078 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9079 if (index != -1) {
9080 return Handle<Object>(frame->GetExpression(index));
9081 }
9082 }
9083
9084 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9085 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9086 NULL);
9087 if (index != -1) {
9088 return Handle<Object>(function_context->get(index));
9089 }
9090 }
9091
9092 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009093 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9094 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009095
9096 AssertNoAllocation no_gc;
9097 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009098 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009099 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009100 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009101 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009102 return arguments;
9103}
9104
9105
9106// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009107// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009108// extension part has all the parameters and locals of the function on the
9109// stack frame. A function which calls eval with the code to evaluate is then
9110// compiled in this context and called in this context. As this context
9111// replaces the context of the function on the stack frame a new (empty)
9112// function is created as well to be used as the closure for the context.
9113// This function and the context acts as replacements for the function on the
9114// stack frame presenting the same view of the values of parameters and
9115// local variables as if the piece of JavaScript was evaluated at the point
9116// where the function on the stack frame is currently stopped.
9117static Object* Runtime_DebugEvaluate(Arguments args) {
9118 HandleScope scope;
9119
9120 // Check the execution state and decode arguments frame and source to be
9121 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009122 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009123 Object* check_result = Runtime_CheckExecutionState(args);
9124 if (check_result->IsFailure()) return check_result;
9125 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9126 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009127 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9128
9129 // Handle the processing of break.
9130 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009131
9132 // Get the frame where the debugging is performed.
9133 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9134 JavaScriptFrameIterator it(id);
9135 JavaScriptFrame* frame = it.frame();
9136 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9137 Handle<Code> code(function->code());
9138 ScopeInfo<> sinfo(*code);
9139
9140 // Traverse the saved contexts chain to find the active context for the
9141 // selected frame.
9142 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009143 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009144 save = save->prev();
9145 }
9146 ASSERT(save != NULL);
9147 SaveContext savex;
9148 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009149
9150 // Create the (empty) function replacing the function on the stack frame for
9151 // the purpose of evaluating in the context created below. It is important
9152 // that this function does not describe any parameters and local variables
9153 // in the context. If it does then this will cause problems with the lookup
9154 // in Context::Lookup, where context slots for parameters and local variables
9155 // are looked at before the extension object.
9156 Handle<JSFunction> go_between =
9157 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9158 go_between->set_context(function->context());
9159#ifdef DEBUG
9160 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9161 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9162 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9163#endif
9164
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009165 // Materialize the content of the local scope into a JSObject.
9166 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009167
9168 // Allocate a new context for the debug evaluation and set the extension
9169 // object build.
9170 Handle<Context> context =
9171 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009172 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009173 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009174 Handle<Context> frame_context(Context::cast(frame->context()));
9175 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009176 context = CopyWithContextChain(frame_context, context);
9177
9178 // Wrap the evaluation statement in a new function compiled in the newly
9179 // created context. The function has one parameter which has to be called
9180 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009181 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009182 // function(arguments,__source__) {return eval(__source__);}
9183 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009184 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009185 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009186 Handle<String> function_source =
9187 Factory::NewStringFromAscii(Vector<const char>(source_str,
9188 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009189 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009190 Compiler::CompileEval(function_source,
9191 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009192 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009193 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009194 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009195 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009196 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009197
9198 // Invoke the result of the compilation to get the evaluation function.
9199 bool has_pending_exception;
9200 Handle<Object> receiver(frame->receiver());
9201 Handle<Object> evaluation_function =
9202 Execution::Call(compiled_function, receiver, 0, NULL,
9203 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009204 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009205
9206 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9207 function_context);
9208
9209 // Invoke the evaluation function and return the result.
9210 const int argc = 2;
9211 Object** argv[argc] = { arguments.location(),
9212 Handle<Object>::cast(source).location() };
9213 Handle<Object> result =
9214 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9215 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009216 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009217
9218 // Skip the global proxy as it has no properties and always delegates to the
9219 // real global object.
9220 if (result->IsJSGlobalProxy()) {
9221 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9222 }
9223
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009224 return *result;
9225}
9226
9227
9228static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9229 HandleScope scope;
9230
9231 // Check the execution state and decode arguments frame and source to be
9232 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009233 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009234 Object* check_result = Runtime_CheckExecutionState(args);
9235 if (check_result->IsFailure()) return check_result;
9236 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009237 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9238
9239 // Handle the processing of break.
9240 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009241
9242 // Enter the top context from before the debugger was invoked.
9243 SaveContext save;
9244 SaveContext* top = &save;
9245 while (top != NULL && *top->context() == *Debug::debug_context()) {
9246 top = top->prev();
9247 }
9248 if (top != NULL) {
9249 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009250 }
9251
9252 // Get the global context now set to the top context from before the
9253 // debugger was invoked.
9254 Handle<Context> context = Top::global_context();
9255
9256 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009257 Handle<SharedFunctionInfo> shared =
9258 Compiler::CompileEval(source,
9259 context,
9260 true,
9261 Compiler::DONT_VALIDATE_JSON);
9262 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009263 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009264 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9265 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009266
9267 // Invoke the result of the compilation to get the evaluation function.
9268 bool has_pending_exception;
9269 Handle<Object> receiver = Top::global();
9270 Handle<Object> result =
9271 Execution::Call(compiled_function, receiver, 0, NULL,
9272 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009273 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009274 return *result;
9275}
9276
9277
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009278static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9279 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009280 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009281
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009282 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009283 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009284
9285 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009286 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009287 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9288 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9289 // because using
9290 // instances->set(i, *GetScriptWrapper(script))
9291 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9292 // already have deferenced the instances handle.
9293 Handle<JSValue> wrapper = GetScriptWrapper(script);
9294 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009295 }
9296
9297 // Return result as a JS array.
9298 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9299 Handle<JSArray>::cast(result)->SetContent(*instances);
9300 return *result;
9301}
9302
9303
9304// Helper function used by Runtime_DebugReferencedBy below.
9305static int DebugReferencedBy(JSObject* target,
9306 Object* instance_filter, int max_references,
9307 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009308 JSFunction* arguments_function) {
9309 NoHandleAllocation ha;
9310 AssertNoAllocation no_alloc;
9311
9312 // Iterate the heap.
9313 int count = 0;
9314 JSObject* last = NULL;
9315 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009316 HeapObject* heap_obj = NULL;
9317 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009318 (max_references == 0 || count < max_references)) {
9319 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009320 if (heap_obj->IsJSObject()) {
9321 // Skip context extension objects and argument arrays as these are
9322 // checked in the context of functions using them.
9323 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009324 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009325 obj->map()->constructor() == arguments_function) {
9326 continue;
9327 }
9328
9329 // Check if the JS object has a reference to the object looked for.
9330 if (obj->ReferencesObject(target)) {
9331 // Check instance filter if supplied. This is normally used to avoid
9332 // references from mirror objects (see Runtime_IsInPrototypeChain).
9333 if (!instance_filter->IsUndefined()) {
9334 Object* V = obj;
9335 while (true) {
9336 Object* prototype = V->GetPrototype();
9337 if (prototype->IsNull()) {
9338 break;
9339 }
9340 if (instance_filter == prototype) {
9341 obj = NULL; // Don't add this object.
9342 break;
9343 }
9344 V = prototype;
9345 }
9346 }
9347
9348 if (obj != NULL) {
9349 // Valid reference found add to instance array if supplied an update
9350 // count.
9351 if (instances != NULL && count < instances_size) {
9352 instances->set(count, obj);
9353 }
9354 last = obj;
9355 count++;
9356 }
9357 }
9358 }
9359 }
9360
9361 // Check for circular reference only. This can happen when the object is only
9362 // referenced from mirrors and has a circular reference in which case the
9363 // object is not really alive and would have been garbage collected if not
9364 // referenced from the mirror.
9365 if (count == 1 && last == target) {
9366 count = 0;
9367 }
9368
9369 // Return the number of referencing objects found.
9370 return count;
9371}
9372
9373
9374// Scan the heap for objects with direct references to an object
9375// args[0]: the object to find references to
9376// args[1]: constructor function for instances to exclude (Mirror)
9377// args[2]: the the maximum number of objects to return
9378static Object* Runtime_DebugReferencedBy(Arguments args) {
9379 ASSERT(args.length() == 3);
9380
9381 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009382 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009383
9384 // Check parameters.
9385 CONVERT_CHECKED(JSObject, target, args[0]);
9386 Object* instance_filter = args[1];
9387 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9388 instance_filter->IsJSObject());
9389 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9390 RUNTIME_ASSERT(max_references >= 0);
9391
9392 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009393 JSObject* arguments_boilerplate =
9394 Top::context()->global_context()->arguments_boilerplate();
9395 JSFunction* arguments_function =
9396 JSFunction::cast(arguments_boilerplate->map()->constructor());
9397
9398 // Get the number of referencing objects.
9399 int count;
9400 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009401 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009402
9403 // Allocate an array to hold the result.
9404 Object* object = Heap::AllocateFixedArray(count);
9405 if (object->IsFailure()) return object;
9406 FixedArray* instances = FixedArray::cast(object);
9407
9408 // Fill the referencing objects.
9409 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009410 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009411
9412 // Return result as JS array.
9413 Object* result =
9414 Heap::AllocateJSObject(
9415 Top::context()->global_context()->array_function());
9416 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9417 return result;
9418}
9419
9420
9421// Helper function used by Runtime_DebugConstructedBy below.
9422static int DebugConstructedBy(JSFunction* constructor, int max_references,
9423 FixedArray* instances, int instances_size) {
9424 AssertNoAllocation no_alloc;
9425
9426 // Iterate the heap.
9427 int count = 0;
9428 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009429 HeapObject* heap_obj = NULL;
9430 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009431 (max_references == 0 || count < max_references)) {
9432 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009433 if (heap_obj->IsJSObject()) {
9434 JSObject* obj = JSObject::cast(heap_obj);
9435 if (obj->map()->constructor() == constructor) {
9436 // Valid reference found add to instance array if supplied an update
9437 // count.
9438 if (instances != NULL && count < instances_size) {
9439 instances->set(count, obj);
9440 }
9441 count++;
9442 }
9443 }
9444 }
9445
9446 // Return the number of referencing objects found.
9447 return count;
9448}
9449
9450
9451// Scan the heap for objects constructed by a specific function.
9452// args[0]: the constructor to find instances of
9453// args[1]: the the maximum number of objects to return
9454static Object* Runtime_DebugConstructedBy(Arguments args) {
9455 ASSERT(args.length() == 2);
9456
9457 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009458 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009459
9460 // Check parameters.
9461 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9462 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9463 RUNTIME_ASSERT(max_references >= 0);
9464
9465 // Get the number of referencing objects.
9466 int count;
9467 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9468
9469 // Allocate an array to hold the result.
9470 Object* object = Heap::AllocateFixedArray(count);
9471 if (object->IsFailure()) return object;
9472 FixedArray* instances = FixedArray::cast(object);
9473
9474 // Fill the referencing objects.
9475 count = DebugConstructedBy(constructor, max_references, instances, count);
9476
9477 // Return result as JS array.
9478 Object* result =
9479 Heap::AllocateJSObject(
9480 Top::context()->global_context()->array_function());
9481 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9482 return result;
9483}
9484
9485
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009486// Find the effective prototype object as returned by __proto__.
9487// args[0]: the object to find the prototype for.
9488static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009489 ASSERT(args.length() == 1);
9490
9491 CONVERT_CHECKED(JSObject, obj, args[0]);
9492
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009493 // Use the __proto__ accessor.
9494 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009495}
9496
9497
9498static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009499 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009500 CPU::DebugBreak();
9501 return Heap::undefined_value();
9502}
9503
9504
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009505static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009506#ifdef DEBUG
9507 HandleScope scope;
9508 ASSERT(args.length() == 1);
9509 // Get the function and make sure it is compiled.
9510 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009511 Handle<SharedFunctionInfo> shared(func->shared());
9512 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009513 return Failure::Exception();
9514 }
9515 func->code()->PrintLn();
9516#endif // DEBUG
9517 return Heap::undefined_value();
9518}
ager@chromium.org9085a012009-05-11 19:22:57 +00009519
9520
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009521static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9522#ifdef DEBUG
9523 HandleScope scope;
9524 ASSERT(args.length() == 1);
9525 // Get the function and make sure it is compiled.
9526 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009527 Handle<SharedFunctionInfo> shared(func->shared());
9528 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009529 return Failure::Exception();
9530 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009531 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009532#endif // DEBUG
9533 return Heap::undefined_value();
9534}
9535
9536
ager@chromium.org9085a012009-05-11 19:22:57 +00009537static Object* Runtime_FunctionGetInferredName(Arguments args) {
9538 NoHandleAllocation ha;
9539 ASSERT(args.length() == 1);
9540
9541 CONVERT_CHECKED(JSFunction, f, args[0]);
9542 return f->shared()->inferred_name();
9543}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009544
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009545
9546static int FindSharedFunctionInfosForScript(Script* script,
9547 FixedArray* buffer) {
9548 AssertNoAllocation no_allocations;
9549
9550 int counter = 0;
9551 int buffer_size = buffer->length();
9552 HeapIterator iterator;
9553 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9554 ASSERT(obj != NULL);
9555 if (!obj->IsSharedFunctionInfo()) {
9556 continue;
9557 }
9558 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9559 if (shared->script() != script) {
9560 continue;
9561 }
9562 if (counter < buffer_size) {
9563 buffer->set(counter, shared);
9564 }
9565 counter++;
9566 }
9567 return counter;
9568}
9569
9570// For a script finds all SharedFunctionInfo's in the heap that points
9571// to this script. Returns JSArray of SharedFunctionInfo wrapped
9572// in OpaqueReferences.
9573static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9574 Arguments args) {
9575 ASSERT(args.length() == 1);
9576 HandleScope scope;
9577 CONVERT_CHECKED(JSValue, script_value, args[0]);
9578
9579 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9580
9581 const int kBufferSize = 32;
9582
9583 Handle<FixedArray> array;
9584 array = Factory::NewFixedArray(kBufferSize);
9585 int number = FindSharedFunctionInfosForScript(*script, *array);
9586 if (number > kBufferSize) {
9587 array = Factory::NewFixedArray(number);
9588 FindSharedFunctionInfosForScript(*script, *array);
9589 }
9590
9591 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9592 result->set_length(Smi::FromInt(number));
9593
9594 LiveEdit::WrapSharedFunctionInfos(result);
9595
9596 return *result;
9597}
9598
9599// For a script calculates compilation information about all its functions.
9600// The script source is explicitly specified by the second argument.
9601// The source of the actual script is not used, however it is important that
9602// all generated code keeps references to this particular instance of script.
9603// Returns a JSArray of compilation infos. The array is ordered so that
9604// each function with all its descendant is always stored in a continues range
9605// with the function itself going first. The root function is a script function.
9606static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9607 ASSERT(args.length() == 2);
9608 HandleScope scope;
9609 CONVERT_CHECKED(JSValue, script, args[0]);
9610 CONVERT_ARG_CHECKED(String, source, 1);
9611 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9612
9613 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9614
9615 if (Top::has_pending_exception()) {
9616 return Failure::Exception();
9617 }
9618
9619 return result;
9620}
9621
9622// Changes the source of the script to a new_source and creates a new
9623// script representing the old version of the script source.
9624static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9625 ASSERT(args.length() == 3);
9626 HandleScope scope;
9627 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9628 CONVERT_ARG_CHECKED(String, new_source, 1);
9629 CONVERT_ARG_CHECKED(String, old_script_name, 2);
9630 Handle<Script> original_script =
9631 Handle<Script>(Script::cast(original_script_value->value()));
9632
9633 Handle<String> original_source(String::cast(original_script->source()));
9634
9635 original_script->set_source(*new_source);
9636 Handle<Script> old_script = Factory::NewScript(original_source);
9637 old_script->set_name(*old_script_name);
9638 old_script->set_line_offset(original_script->line_offset());
9639 old_script->set_column_offset(original_script->column_offset());
9640 old_script->set_data(original_script->data());
9641 old_script->set_type(original_script->type());
9642 old_script->set_context_data(original_script->context_data());
9643 old_script->set_compilation_type(original_script->compilation_type());
9644 old_script->set_eval_from_shared(original_script->eval_from_shared());
9645 old_script->set_eval_from_instructions_offset(
9646 original_script->eval_from_instructions_offset());
9647
9648
9649 Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
9650
9651 return *(GetScriptWrapper(old_script));
9652}
9653
9654// Replaces code of SharedFunctionInfo with a new one.
9655static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9656 ASSERT(args.length() == 2);
9657 HandleScope scope;
9658 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9659 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9660
9661 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
9662
9663 return Heap::undefined_value();
9664}
9665
9666// Connects SharedFunctionInfo to another script.
9667static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
9668 ASSERT(args.length() == 2);
9669 HandleScope scope;
9670 CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
9671 CONVERT_ARG_CHECKED(JSValue, script_value, 1);
9672 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9673
9674 LiveEdit::RelinkFunctionToScript(shared_info_array, script);
9675
9676 return Heap::undefined_value();
9677}
9678
9679// Updates positions of a shared function info (first parameter) according
9680// to script source change. Text change is described in second parameter as
9681// array of groups of 3 numbers:
9682// (change_begin, change_end, change_end_new_position).
9683// Each group describes a change in text; groups are sorted by change_begin.
9684static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9685 ASSERT(args.length() == 2);
9686 HandleScope scope;
9687 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9688 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9689
9690 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
9691
9692 return Heap::undefined_value();
9693}
9694
9695
9696static LiveEdit::FunctionPatchabilityStatus FindFunctionCodeOnStacks(
9697 Handle<SharedFunctionInfo> shared) {
9698 // TODO(635): check all threads, not only the current one.
9699 for (StackFrameIterator it; !it.done(); it.Advance()) {
9700 StackFrame* frame = it.frame();
9701 if (frame->code() == shared->code()) {
9702 return LiveEdit::FUNCTION_BLOCKED_ON_STACK;
9703 }
9704 }
9705 return LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH;
9706}
9707
9708// For array of SharedFunctionInfo's (each wrapped in JSValue)
9709// checks that none of them have activations on stacks (of any thread).
9710// Returns array of the same length with corresponding results of
9711// LiveEdit::FunctionPatchabilityStatus type.
9712static Object* Runtime_LiveEditCheckStackActivations(Arguments args) {
9713 ASSERT(args.length() == 1);
9714 HandleScope scope;
9715 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9716
9717
9718 int len = Smi::cast(shared_array->length())->value();
9719 Handle<JSArray> result = Factory::NewJSArray(len);
9720
9721 for (int i = 0; i < len; i++) {
9722 JSValue* wrapper = JSValue::cast(shared_array->GetElement(i));
9723 Handle<SharedFunctionInfo> shared(
9724 SharedFunctionInfo::cast(wrapper->value()));
9725 LiveEdit::FunctionPatchabilityStatus check_res =
9726 FindFunctionCodeOnStacks(shared);
9727 SetElement(result, i, Handle<Smi>(Smi::FromInt(check_res)));
9728 }
9729
9730 return *result;
9731}
9732
9733
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009734// A testing entry. Returns statement position which is the closest to
9735// source_position.
9736static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9737 ASSERT(args.length() == 2);
9738 HandleScope scope;
9739 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9740 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9741
9742 Handle<Code> code(function->code());
9743
9744 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9745 int closest_pc = 0;
9746 int distance = kMaxInt;
9747 while (!it.done()) {
9748 int statement_position = static_cast<int>(it.rinfo()->data());
9749 // Check if this break point is closer that what was previously found.
9750 if (source_position <= statement_position &&
9751 statement_position - source_position < distance) {
9752 closest_pc = it.rinfo()->pc() - code->instruction_start();
9753 distance = statement_position - source_position;
9754 // Check whether we can't get any closer.
9755 if (distance == 0) break;
9756 }
9757 it.next();
9758 }
9759
9760 return Smi::FromInt(closest_pc);
9761}
9762
9763
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009764#endif // ENABLE_DEBUGGER_SUPPORT
9765
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009766#ifdef ENABLE_LOGGING_AND_PROFILING
9767
9768static Object* Runtime_ProfilerResume(Arguments args) {
9769 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009770 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009771
9772 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009773 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9774 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009775 return Heap::undefined_value();
9776}
9777
9778
9779static Object* Runtime_ProfilerPause(Arguments args) {
9780 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009781 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009782
9783 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009784 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9785 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009786 return Heap::undefined_value();
9787}
9788
9789#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009790
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009791// Finds the script object from the script data. NOTE: This operation uses
9792// heap traversal to find the function generated for the source position
9793// for the requested break point. For lazily compiled functions several heap
9794// traversals might be required rendering this operation as a rather slow
9795// operation. However for setting break points which is normally done through
9796// some kind of user interaction the performance is not crucial.
9797static Handle<Object> Runtime_GetScriptFromScriptName(
9798 Handle<String> script_name) {
9799 // Scan the heap for Script objects to find the script with the requested
9800 // script data.
9801 Handle<Script> script;
9802 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009803 HeapObject* obj = NULL;
9804 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009805 // If a script is found check if it has the script data requested.
9806 if (obj->IsScript()) {
9807 if (Script::cast(obj)->name()->IsString()) {
9808 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9809 script = Handle<Script>(Script::cast(obj));
9810 }
9811 }
9812 }
9813 }
9814
9815 // If no script with the requested script data is found return undefined.
9816 if (script.is_null()) return Factory::undefined_value();
9817
9818 // Return the script found.
9819 return GetScriptWrapper(script);
9820}
9821
9822
9823// Get the script object from script data. NOTE: Regarding performance
9824// see the NOTE for GetScriptFromScriptData.
9825// args[0]: script data for the script to find the source for
9826static Object* Runtime_GetScript(Arguments args) {
9827 HandleScope scope;
9828
9829 ASSERT(args.length() == 1);
9830
9831 CONVERT_CHECKED(String, script_name, args[0]);
9832
9833 // Find the requested script.
9834 Handle<Object> result =
9835 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
9836 return *result;
9837}
9838
9839
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009840// Determines whether the given stack frame should be displayed in
9841// a stack trace. The caller is the error constructor that asked
9842// for the stack trace to be collected. The first time a construct
9843// call to this function is encountered it is skipped. The seen_caller
9844// in/out parameter is used to remember if the caller has been seen
9845// yet.
9846static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
9847 bool* seen_caller) {
9848 // Only display JS frames.
9849 if (!raw_frame->is_java_script())
9850 return false;
9851 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
9852 Object* raw_fun = frame->function();
9853 // Not sure when this can happen but skip it just in case.
9854 if (!raw_fun->IsJSFunction())
9855 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009856 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009857 *seen_caller = true;
9858 return false;
9859 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009860 // Skip all frames until we've seen the caller. Also, skip the most
9861 // obvious builtin calls. Some builtin calls (such as Number.ADD
9862 // which is invoked using 'call') are very difficult to recognize
9863 // so we're leaving them in for now.
9864 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009865}
9866
9867
9868// Collect the raw data for a stack trace. Returns an array of three
9869// element segments each containing a receiver, function and native
9870// code offset.
9871static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009872 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009873 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009874 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
9875
9876 HandleScope scope;
9877
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009878 limit = Max(limit, 0); // Ensure that limit is not negative.
9879 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009880 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009881
9882 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009883 // If the caller parameter is a function we skip frames until we're
9884 // under it before starting to collect.
9885 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009886 int cursor = 0;
9887 int frames_seen = 0;
9888 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009889 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009890 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009891 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009892 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009893 Object* recv = frame->receiver();
9894 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009895 Address pc = frame->pc();
9896 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009897 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009898 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009899 if (cursor + 2 < elements->length()) {
9900 elements->set(cursor++, recv);
9901 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009902 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009903 } else {
9904 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009905 Handle<Object> recv_handle(recv);
9906 Handle<Object> fun_handle(fun);
9907 SetElement(result, cursor++, recv_handle);
9908 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009909 SetElement(result, cursor++, Handle<Smi>(offset));
9910 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009911 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009912 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009913 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009914
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009915 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009916 return *result;
9917}
9918
9919
ager@chromium.org3811b432009-10-28 14:53:37 +00009920// Returns V8 version as a string.
9921static Object* Runtime_GetV8Version(Arguments args) {
9922 ASSERT_EQ(args.length(), 0);
9923
9924 NoHandleAllocation ha;
9925
9926 const char* version_string = v8::V8::GetVersion();
9927
9928 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
9929}
9930
9931
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009932static Object* Runtime_Abort(Arguments args) {
9933 ASSERT(args.length() == 2);
9934 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
9935 Smi::cast(args[1])->value());
9936 Top::PrintStack();
9937 OS::Abort();
9938 UNREACHABLE();
9939 return NULL;
9940}
9941
9942
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009943static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
9944 ASSERT(args.length() == 0);
9945 HandleScope::DeleteExtensions();
9946 return Heap::undefined_value();
9947}
9948
9949
kasper.lund44510672008-07-25 07:37:58 +00009950#ifdef DEBUG
9951// ListNatives is ONLY used by the fuzz-natives.js in debug mode
9952// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009953static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009954 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009955 HandleScope scope;
9956 Handle<JSArray> result = Factory::NewJSArray(0);
9957 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009958 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +00009959#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009960 { \
9961 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009962 Handle<String> name; \
9963 /* Inline runtime functions have an underscore in front of the name. */ \
9964 if (inline_runtime_functions) { \
9965 name = Factory::NewStringFromAscii( \
9966 Vector<const char>("_" #Name, StrLength("_" #Name))); \
9967 } else { \
9968 name = Factory::NewStringFromAscii( \
9969 Vector<const char>(#Name, StrLength(#Name))); \
9970 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009971 Handle<JSArray> pair = Factory::NewJSArray(0); \
9972 SetElement(pair, 0, name); \
9973 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
9974 SetElement(result, index++, pair); \
9975 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009976 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009977 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009978 inline_runtime_functions = true;
9979 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009980#undef ADD_ENTRY
9981 return *result;
9982}
kasper.lund44510672008-07-25 07:37:58 +00009983#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009984
9985
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009986static Object* Runtime_Log(Arguments args) {
9987 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00009988 CONVERT_CHECKED(String, format, args[0]);
9989 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009990 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009991 Logger::LogRuntime(chars, elms);
9992 return Heap::undefined_value();
9993}
9994
9995
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009996static Object* Runtime_IS_VAR(Arguments args) {
9997 UNREACHABLE(); // implemented as macro in the parser
9998 return NULL;
9999}
10000
10001
10002// ----------------------------------------------------------------------------
10003// Implementation of Runtime
10004
ager@chromium.orga1645e22009-09-09 19:27:10 +000010005#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010006 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010007 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010008
10009static Runtime::Function Runtime_functions[] = {
10010 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010011 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010012};
10013
10014#undef F
10015
10016
10017Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10018 ASSERT(0 <= fid && fid < kNofFunctions);
10019 return &Runtime_functions[fid];
10020}
10021
10022
10023Runtime::Function* Runtime::FunctionForName(const char* name) {
10024 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10025 if (strcmp(f->name, name) == 0) {
10026 return f;
10027 }
10028 }
10029 return NULL;
10030}
10031
10032
10033void Runtime::PerformGC(Object* result) {
10034 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010035 if (failure->IsRetryAfterGC()) {
10036 // Try to do a garbage collection; ignore it if it fails. The C
10037 // entry stub will throw an out-of-memory exception in that case.
10038 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10039 } else {
10040 // Handle last resort GC and make sure to allow future allocations
10041 // to grow the heap without causing GCs (if possible).
10042 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010043 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010044 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010045}
10046
10047
10048} } // namespace v8::internal