blob: 8625053894abda8a5bd52c7cbcb41f1c66428c20 [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);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000294 } else if (key->ToArrayIndex(&element_index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 // 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
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000572// Enumerator used as indices into the array returned from GetOwnProperty
573enum PropertyDescriptorIndices {
574 IS_ACCESSOR_INDEX,
575 VALUE_INDEX,
576 GETTER_INDEX,
577 SETTER_INDEX,
578 WRITABLE_INDEX,
579 ENUMERABLE_INDEX,
580 CONFIGURABLE_INDEX,
581 DESCRIPTOR_SIZE
582};
583
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000584// Returns an array with the property description:
585// if args[1] is not a property on args[0]
586// returns undefined
587// if args[1] is a data property on args[0]
588// [false, value, Writeable, Enumerable, Configurable]
589// if args[1] is an accessor on args[0]
590// [true, GetFunction, SetFunction, Enumerable, Configurable]
591static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000592 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000593 HandleScope scope;
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000594 Handle<FixedArray> elms = Factory::NewFixedArray(DESCRIPTOR_SIZE);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000595 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
596 LookupResult result;
597 CONVERT_CHECKED(JSObject, obj, args[0]);
598 CONVERT_CHECKED(String, name, args[1]);
599
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000600 // This could be an element.
601 uint32_t index;
602 if (name->AsArrayIndex(&index)) {
603 if (!obj->HasLocalElement(index)) {
604 return Heap::undefined_value();
605 }
606
607 // Special handling of string objects according to ECMAScript 5 15.5.5.2.
608 // Note that this might be a string object with elements other than the
609 // actual string value. This is covered by the subsequent cases.
610 if (obj->IsStringObjectWithCharacterAt(index)) {
611 JSValue* js_value = JSValue::cast(obj);
612 String* str = String::cast(js_value->value());
613 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
614 elms->set(VALUE_INDEX, str->SubString(index, index+1));
615 elms->set(WRITABLE_INDEX, Heap::false_value());
616 elms->set(ENUMERABLE_INDEX, Heap::false_value());
617 elms->set(CONFIGURABLE_INDEX, Heap::false_value());
618 return *desc;
619 }
620
621 // This can potentially be an element in the elements dictionary or
622 // a fast element.
623 if (obj->HasDictionaryElements()) {
624 NumberDictionary* dictionary = obj->element_dictionary();
625 int entry = dictionary->FindEntry(index);
626 PropertyDetails details = dictionary->DetailsAt(entry);
627 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
628 elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000629 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000630 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000631 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000632 return *desc;
633 } else {
634 // Elements that are stored as array elements always has:
635 // writable: true, configurable: true, enumerable: true.
636 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
637 elms->set(VALUE_INDEX, obj->GetElement(index));
638 elms->set(WRITABLE_INDEX, Heap::true_value());
639 elms->set(ENUMERABLE_INDEX, Heap::true_value());
640 elms->set(CONFIGURABLE_INDEX, Heap::true_value());
641 return *desc;
642 }
643 }
644
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000645 // Use recursive implementation to also traverse hidden prototypes
646 GetOwnPropertyImplementation(obj, name, &result);
647
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000648 if (!result.IsProperty()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000649 return Heap::undefined_value();
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000650 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000651 if (result.type() == CALLBACKS) {
652 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000653 if (structure->IsProxy() || structure->IsAccessorInfo()) {
654 // Property that is internally implemented as a callback or
655 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000656 Object* value = obj->GetPropertyWithCallback(
657 obj, structure, name, result.holder());
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000658 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
659 elms->set(VALUE_INDEX, value);
660 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000661 } else if (structure->IsFixedArray()) {
662 // __defineGetter__/__defineSetter__ callback.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000663 elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
664 elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
665 elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000666 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000667 return Heap::undefined_value();
668 }
669 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000670 elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
671 elms->set(VALUE_INDEX, result.GetLazyValue());
672 elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000673 }
674
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000675 elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
676 elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000677 return *desc;
678}
679
680
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000681static Object* Runtime_IsExtensible(Arguments args) {
682 ASSERT(args.length() == 1);
683 CONVERT_CHECKED(JSObject, obj, args[0]);
684 return obj->map()->is_extensible() ? Heap::true_value()
685 : Heap::false_value();
686}
687
688
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000689static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000690 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000692 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
693 CONVERT_ARG_CHECKED(String, pattern, 1);
694 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000695 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
696 if (result.is_null()) return Failure::Exception();
697 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000698}
699
700
701static Object* Runtime_CreateApiFunction(Arguments args) {
702 HandleScope scope;
703 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000704 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 return *Factory::CreateApiFunction(data);
706}
707
708
709static Object* Runtime_IsTemplate(Arguments args) {
710 ASSERT(args.length() == 1);
711 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000712 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713 return Heap::ToBoolean(result);
714}
715
716
717static Object* Runtime_GetTemplateField(Arguments args) {
718 ASSERT(args.length() == 2);
719 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000720 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000721 int index = field->value();
722 int offset = index * kPointerSize + HeapObject::kHeaderSize;
723 InstanceType type = templ->map()->instance_type();
724 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
725 type == OBJECT_TEMPLATE_INFO_TYPE);
726 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000727 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000728 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
729 } else {
730 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
731 }
732 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733}
734
735
ager@chromium.org870a0b62008-11-04 11:43:05 +0000736static Object* Runtime_DisableAccessChecks(Arguments args) {
737 ASSERT(args.length() == 1);
738 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000739 Map* old_map = object->map();
740 bool needs_access_checks = old_map->is_access_check_needed();
741 if (needs_access_checks) {
742 // Copy map so it won't interfere constructor's initial map.
743 Object* new_map = old_map->CopyDropTransitions();
744 if (new_map->IsFailure()) return new_map;
745
746 Map::cast(new_map)->set_is_access_check_needed(false);
747 object->set_map(Map::cast(new_map));
748 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000749 return needs_access_checks ? Heap::true_value() : Heap::false_value();
750}
751
752
753static Object* Runtime_EnableAccessChecks(Arguments args) {
754 ASSERT(args.length() == 1);
755 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000756 Map* old_map = object->map();
757 if (!old_map->is_access_check_needed()) {
758 // Copy map so it won't interfere constructor's initial map.
759 Object* new_map = old_map->CopyDropTransitions();
760 if (new_map->IsFailure()) return new_map;
761
762 Map::cast(new_map)->set_is_access_check_needed(true);
763 object->set_map(Map::cast(new_map));
764 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000765 return Heap::undefined_value();
766}
767
768
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
770 HandleScope scope;
771 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
772 Handle<Object> args[2] = { type_handle, name };
773 Handle<Object> error =
774 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
775 return Top::Throw(*error);
776}
777
778
779static Object* Runtime_DeclareGlobals(Arguments args) {
780 HandleScope scope;
781 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
782
ager@chromium.org3811b432009-10-28 14:53:37 +0000783 Handle<Context> context = args.at<Context>(0);
784 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000785 bool is_eval = Smi::cast(args[2])->value() == 1;
786
787 // Compute the property attributes. According to ECMA-262, section
788 // 13, page 71, the property must be read-only and
789 // non-deletable. However, neither SpiderMonkey nor KJS creates the
790 // property as read-only, so we don't either.
791 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 // Traverse the name/value pairs and set the properties.
794 int length = pairs->length();
795 for (int i = 0; i < length; i += 2) {
796 HandleScope scope;
797 Handle<String> name(String::cast(pairs->get(i)));
798 Handle<Object> value(pairs->get(i + 1));
799
800 // We have to declare a global const property. To capture we only
801 // assign to it when evaluating the assignment for "const x =
802 // <expr>" the initial value is the hole.
803 bool is_const_property = value->IsTheHole();
804
805 if (value->IsUndefined() || is_const_property) {
806 // Lookup the property in the global object, and don't set the
807 // value of the variable if the property is already there.
808 LookupResult lookup;
809 global->Lookup(*name, &lookup);
810 if (lookup.IsProperty()) {
811 // Determine if the property is local by comparing the holder
812 // against the global object. The information will be used to
813 // avoid throwing re-declaration errors when declaring
814 // variables or constants that exist in the prototype chain.
815 bool is_local = (*global == lookup.holder());
816 // Get the property attributes and determine if the property is
817 // read-only.
818 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
819 bool is_read_only = (attributes & READ_ONLY) != 0;
820 if (lookup.type() == INTERCEPTOR) {
821 // If the interceptor says the property is there, we
822 // just return undefined without overwriting the property.
823 // Otherwise, we continue to setting the property.
824 if (attributes != ABSENT) {
825 // Check if the existing property conflicts with regards to const.
826 if (is_local && (is_read_only || is_const_property)) {
827 const char* type = (is_read_only) ? "const" : "var";
828 return ThrowRedeclarationError(type, name);
829 };
830 // The property already exists without conflicting: Go to
831 // the next declaration.
832 continue;
833 }
834 // Fall-through and introduce the absent property by using
835 // SetProperty.
836 } else {
837 if (is_local && (is_read_only || is_const_property)) {
838 const char* type = (is_read_only) ? "const" : "var";
839 return ThrowRedeclarationError(type, name);
840 }
841 // The property already exists without conflicting: Go to
842 // the next declaration.
843 continue;
844 }
845 }
846 } else {
847 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000848 Handle<SharedFunctionInfo> shared =
849 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000851 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 value = function;
853 }
854
855 LookupResult lookup;
856 global->LocalLookup(*name, &lookup);
857
858 PropertyAttributes attributes = is_const_property
859 ? static_cast<PropertyAttributes>(base | READ_ONLY)
860 : base;
861
862 if (lookup.IsProperty()) {
863 // There's a local property that we need to overwrite because
864 // we're either declaring a function or there's an interceptor
865 // that claims the property is absent.
866
867 // Check for conflicting re-declarations. We cannot have
868 // conflicting types in case of intercepted properties because
869 // they are absent.
870 if (lookup.type() != INTERCEPTOR &&
871 (lookup.IsReadOnly() || is_const_property)) {
872 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
873 return ThrowRedeclarationError(type, name);
874 }
875 SetProperty(global, name, value, attributes);
876 } else {
877 // If a property with this name does not already exist on the
878 // global object add the property locally. We take special
879 // precautions to always add it as a local property even in case
880 // of callbacks in the prototype chain (this rules out using
881 // SetProperty). Also, we must use the handle-based version to
882 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000883 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884 }
885 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000886
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887 return Heap::undefined_value();
888}
889
890
891static Object* Runtime_DeclareContextSlot(Arguments args) {
892 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000893 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000894
ager@chromium.org7c537e22008-10-16 08:43:32 +0000895 CONVERT_ARG_CHECKED(Context, context, 0);
896 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000897 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000898 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000899 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000900 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000901
902 // Declarations are always done in the function context.
903 context = Handle<Context>(context->fcontext());
904
905 int index;
906 PropertyAttributes attributes;
907 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000908 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 context->Lookup(name, flags, &index, &attributes);
910
911 if (attributes != ABSENT) {
912 // The name was declared before; check for conflicting
913 // re-declarations: This is similar to the code in parser.cc in
914 // the AstBuildingParser::Declare function.
915 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
916 // Functions are not read-only.
917 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
918 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
919 return ThrowRedeclarationError(type, name);
920 }
921
922 // Initialize it if necessary.
923 if (*initial_value != NULL) {
924 if (index >= 0) {
925 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000926 // the function context or the arguments object.
927 if (holder->IsContext()) {
928 ASSERT(holder.is_identical_to(context));
929 if (((attributes & READ_ONLY) == 0) ||
930 context->get(index)->IsTheHole()) {
931 context->set(index, *initial_value);
932 }
933 } else {
934 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000935 }
936 } else {
937 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000938 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939 SetProperty(context_ext, name, initial_value, mode);
940 }
941 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000943 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000944 // The property is not in the function context. It needs to be
945 // "declared" in the function context's extension context, or in the
946 // global context.
947 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000948 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000949 // The function context's extension context exists - use it.
950 context_ext = Handle<JSObject>(context->extension());
951 } else {
952 // The function context's extension context does not exists - allocate
953 // it.
954 context_ext = Factory::NewJSObject(Top::context_extension_function());
955 // And store it in the extension slot.
956 context->set_extension(*context_ext);
957 }
958 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959
ager@chromium.org7c537e22008-10-16 08:43:32 +0000960 // Declare the property by setting it to the initial value if provided,
961 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
962 // constant declarations).
963 ASSERT(!context_ext->HasLocalProperty(*name));
964 Handle<Object> value(Heap::undefined_value());
965 if (*initial_value != NULL) value = initial_value;
966 SetProperty(context_ext, name, value, mode);
967 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
968 }
969
970 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000971}
972
973
974static Object* Runtime_InitializeVarGlobal(Arguments args) {
975 NoHandleAllocation nha;
976
977 // Determine if we need to assign to the variable if it already
978 // exists (based on the number of arguments).
979 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
980 bool assign = args.length() == 2;
981
982 CONVERT_ARG_CHECKED(String, name, 0);
983 GlobalObject* global = Top::context()->global();
984
985 // According to ECMA-262, section 12.2, page 62, the property must
986 // not be deletable.
987 PropertyAttributes attributes = DONT_DELETE;
988
989 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000990 // there, there is a property with this name in the prototype chain.
991 // We follow Safari and Firefox behavior and only set the property
992 // locally if there is an explicit initialization value that we have
993 // to assign to the property. When adding the property we take
994 // special precautions to always add it as a local property even in
995 // case of callbacks in the prototype chain (this rules out using
996 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
997 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000998 // Note that objects can have hidden prototypes, so we need to traverse
999 // the whole chain of hidden prototypes to do a 'local' lookup.
1000 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001001 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001002 while (true) {
1003 real_holder->LocalLookup(*name, &lookup);
1004 if (lookup.IsProperty()) {
1005 // Determine if this is a redeclaration of something read-only.
1006 if (lookup.IsReadOnly()) {
1007 // If we found readonly property on one of hidden prototypes,
1008 // just shadow it.
1009 if (real_holder != Top::context()->global()) break;
1010 return ThrowRedeclarationError("const", name);
1011 }
1012
1013 // Determine if this is a redeclaration of an intercepted read-only
1014 // property and figure out if the property exists at all.
1015 bool found = true;
1016 PropertyType type = lookup.type();
1017 if (type == INTERCEPTOR) {
1018 HandleScope handle_scope;
1019 Handle<JSObject> holder(real_holder);
1020 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
1021 real_holder = *holder;
1022 if (intercepted == ABSENT) {
1023 // The interceptor claims the property isn't there. We need to
1024 // make sure to introduce it.
1025 found = false;
1026 } else if ((intercepted & READ_ONLY) != 0) {
1027 // The property is present, but read-only. Since we're trying to
1028 // overwrite it with a variable declaration we must throw a
1029 // re-declaration error. However if we found readonly property
1030 // on one of hidden prototypes, just shadow it.
1031 if (real_holder != Top::context()->global()) break;
1032 return ThrowRedeclarationError("const", name);
1033 }
1034 }
1035
1036 if (found && !assign) {
1037 // The global property is there and we're not assigning any value
1038 // to it. Just return.
1039 return Heap::undefined_value();
1040 }
1041
1042 // Assign the value (or undefined) to the property.
1043 Object* value = (assign) ? args[1] : Heap::undefined_value();
1044 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001045 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001046
1047 Object* proto = real_holder->GetPrototype();
1048 if (!proto->IsJSObject())
1049 break;
1050
1051 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
1052 break;
1053
1054 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001055 }
1056
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001057 global = Top::context()->global();
1058 if (assign) {
1059 return global->IgnoreAttributesAndSetLocalProperty(*name,
1060 args[1],
1061 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001062 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001063 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001064}
1065
1066
1067static Object* Runtime_InitializeConstGlobal(Arguments args) {
1068 // All constants are declared with an initial value. The name
1069 // of the constant is the first argument and the initial value
1070 // is the second.
1071 RUNTIME_ASSERT(args.length() == 2);
1072 CONVERT_ARG_CHECKED(String, name, 0);
1073 Handle<Object> value = args.at<Object>(1);
1074
1075 // Get the current global object from top.
1076 GlobalObject* global = Top::context()->global();
1077
1078 // According to ECMA-262, section 12.2, page 62, the property must
1079 // not be deletable. Since it's a const, it must be READ_ONLY too.
1080 PropertyAttributes attributes =
1081 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1082
1083 // Lookup the property locally in the global object. If it isn't
1084 // there, we add the property and take special precautions to always
1085 // add it as a local property even in case of callbacks in the
1086 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001087 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001088 LookupResult lookup;
1089 global->LocalLookup(*name, &lookup);
1090 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001091 return global->IgnoreAttributesAndSetLocalProperty(*name,
1092 *value,
1093 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001094 }
1095
1096 // Determine if this is a redeclaration of something not
1097 // read-only. In case the result is hidden behind an interceptor we
1098 // need to ask it for the property attributes.
1099 if (!lookup.IsReadOnly()) {
1100 if (lookup.type() != INTERCEPTOR) {
1101 return ThrowRedeclarationError("var", name);
1102 }
1103
1104 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1105
1106 // Throw re-declaration error if the intercepted property is present
1107 // but not read-only.
1108 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1109 return ThrowRedeclarationError("var", name);
1110 }
1111
1112 // Restore global object from context (in case of GC) and continue
1113 // with setting the value because the property is either absent or
1114 // read-only. We also have to do redo the lookup.
1115 global = Top::context()->global();
1116
1117 // BUG 1213579: Handle the case where we have to set a read-only
1118 // property through an interceptor and only do it if it's
1119 // uninitialized, e.g. the hole. Nirk...
1120 global->SetProperty(*name, *value, attributes);
1121 return *value;
1122 }
1123
1124 // Set the value, but only we're assigning the initial value to a
1125 // constant. For now, we determine this by checking if the
1126 // current value is the hole.
1127 PropertyType type = lookup.type();
1128 if (type == FIELD) {
1129 FixedArray* properties = global->properties();
1130 int index = lookup.GetFieldIndex();
1131 if (properties->get(index)->IsTheHole()) {
1132 properties->set(index, *value);
1133 }
1134 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001135 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1136 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001137 }
1138 } else {
1139 // Ignore re-initialization of constants that have already been
1140 // assigned a function value.
1141 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1142 }
1143
1144 // Use the set value as the result of the operation.
1145 return *value;
1146}
1147
1148
1149static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1150 HandleScope scope;
1151 ASSERT(args.length() == 3);
1152
1153 Handle<Object> value(args[0]);
1154 ASSERT(!value->IsTheHole());
1155 CONVERT_ARG_CHECKED(Context, context, 1);
1156 Handle<String> name(String::cast(args[2]));
1157
1158 // Initializations are always done in the function context.
1159 context = Handle<Context>(context->fcontext());
1160
1161 int index;
1162 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001163 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001164 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001165 context->Lookup(name, flags, &index, &attributes);
1166
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001167 // In most situations, the property introduced by the const
1168 // declaration should be present in the context extension object.
1169 // However, because declaration and initialization are separate, the
1170 // property might have been deleted (if it was introduced by eval)
1171 // before we reach the initialization point.
1172 //
1173 // Example:
1174 //
1175 // function f() { eval("delete x; const x;"); }
1176 //
1177 // In that case, the initialization behaves like a normal assignment
1178 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001179 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001180 // Property was found in a context.
1181 if (holder->IsContext()) {
1182 // The holder cannot be the function context. If it is, there
1183 // should have been a const redeclaration error when declaring
1184 // the const property.
1185 ASSERT(!holder.is_identical_to(context));
1186 if ((attributes & READ_ONLY) == 0) {
1187 Handle<Context>::cast(holder)->set(index, *value);
1188 }
1189 } else {
1190 // The holder is an arguments object.
1191 ASSERT((attributes & READ_ONLY) == 0);
1192 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001193 }
1194 return *value;
1195 }
1196
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001197 // The property could not be found, we introduce it in the global
1198 // context.
1199 if (attributes == ABSENT) {
1200 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1201 SetProperty(global, name, value, NONE);
1202 return *value;
1203 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001204
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001205 // The property was present in a context extension object.
1206 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001207
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001208 if (*context_ext == context->extension()) {
1209 // This is the property that was introduced by the const
1210 // declaration. Set it if it hasn't been set before. NOTE: We
1211 // cannot use GetProperty() to get the current value as it
1212 // 'unholes' the value.
1213 LookupResult lookup;
1214 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1215 ASSERT(lookup.IsProperty()); // the property was declared
1216 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1217
1218 PropertyType type = lookup.type();
1219 if (type == FIELD) {
1220 FixedArray* properties = context_ext->properties();
1221 int index = lookup.GetFieldIndex();
1222 if (properties->get(index)->IsTheHole()) {
1223 properties->set(index, *value);
1224 }
1225 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001226 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1227 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001228 }
1229 } else {
1230 // We should not reach here. Any real, named property should be
1231 // either a field or a dictionary slot.
1232 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001233 }
1234 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001235 // The property was found in a different context extension object.
1236 // Set it if it is not a read-only property.
1237 if ((attributes & READ_ONLY) == 0) {
1238 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1239 // Setting a property might throw an exception. Exceptions
1240 // are converted to empty handles in handle operations. We
1241 // need to convert back to exceptions here.
1242 if (set.is_null()) {
1243 ASSERT(Top::has_pending_exception());
1244 return Failure::Exception();
1245 }
1246 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001247 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001248
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 return *value;
1250}
1251
1252
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001253static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1254 Arguments args) {
1255 HandleScope scope;
1256 ASSERT(args.length() == 2);
1257 CONVERT_ARG_CHECKED(JSObject, object, 0);
1258 CONVERT_SMI_CHECKED(properties, args[1]);
1259 if (object->HasFastProperties()) {
1260 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1261 }
1262 return *object;
1263}
1264
1265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001266static Object* Runtime_RegExpExec(Arguments args) {
1267 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001268 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001269 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1270 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001271 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001272 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001273 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001274 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001275 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001276 RUNTIME_ASSERT(index >= 0);
1277 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001278 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001279 Handle<Object> result = RegExpImpl::Exec(regexp,
1280 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001281 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001282 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001283 if (result.is_null()) return Failure::Exception();
1284 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001285}
1286
1287
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001288static Object* Runtime_RegExpConstructResult(Arguments args) {
1289 ASSERT(args.length() == 3);
1290 CONVERT_SMI_CHECKED(elements_count, args[0]);
1291 if (elements_count > JSArray::kMaxFastElementsLength) {
1292 return Top::ThrowIllegalOperation();
1293 }
1294 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1295 if (new_object->IsFailure()) return new_object;
1296 FixedArray* elements = FixedArray::cast(new_object);
1297 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1298 NEW_SPACE,
1299 OLD_POINTER_SPACE);
1300 if (new_object->IsFailure()) return new_object;
1301 {
1302 AssertNoAllocation no_gc;
1303 HandleScope scope;
1304 reinterpret_cast<HeapObject*>(new_object)->
1305 set_map(Top::global_context()->regexp_result_map());
1306 }
1307 JSArray* array = JSArray::cast(new_object);
1308 array->set_properties(Heap::empty_fixed_array());
1309 array->set_elements(elements);
1310 array->set_length(Smi::FromInt(elements_count));
1311 // Write in-object properties after the length of the array.
1312 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1313 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1314 return array;
1315}
1316
1317
lrn@chromium.org25156de2010-04-06 13:10:27 +00001318static Object* Runtime_RegExpInitializeObject(Arguments args) {
1319 AssertNoAllocation no_alloc;
1320 ASSERT(args.length() == 5);
1321 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1322 CONVERT_CHECKED(String, source, args[1]);
1323
1324 Object* global = args[2];
1325 if (!global->IsTrue()) global = Heap::false_value();
1326
1327 Object* ignoreCase = args[3];
1328 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1329
1330 Object* multiline = args[4];
1331 if (!multiline->IsTrue()) multiline = Heap::false_value();
1332
1333 Map* map = regexp->map();
1334 Object* constructor = map->constructor();
1335 if (constructor->IsJSFunction() &&
1336 JSFunction::cast(constructor)->initial_map() == map) {
1337 // If we still have the original map, set in-object properties directly.
1338 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1339 // TODO(lrn): Consider skipping write barrier on booleans as well.
1340 // Both true and false should be in oldspace at all times.
1341 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1342 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1343 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1344 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1345 Smi::FromInt(0),
1346 SKIP_WRITE_BARRIER);
1347 return regexp;
1348 }
1349
1350 // Map has changed, so use generic, but slower, method.
1351 PropertyAttributes final =
1352 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1353 PropertyAttributes writable =
1354 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1355 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1356 source,
1357 final);
1358 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1359 global,
1360 final);
1361 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1362 ignoreCase,
1363 final);
1364 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1365 multiline,
1366 final);
1367 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1368 Smi::FromInt(0),
1369 writable);
1370 return regexp;
1371}
1372
1373
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001374static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1375 HandleScope scope;
1376 ASSERT(args.length() == 1);
1377 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1378 // This is necessary to enable fast checks for absence of elements
1379 // on Array.prototype and below.
1380 prototype->set_elements(Heap::empty_fixed_array());
1381 return Smi::FromInt(0);
1382}
1383
1384
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001385static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1386 const char* name,
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001387 Builtins::Name builtin_name) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001388 Handle<String> key = Factory::LookupAsciiSymbol(name);
1389 Handle<Code> code(Builtins::builtin(builtin_name));
1390 Handle<JSFunction> optimized = Factory::NewFunction(key,
1391 JS_OBJECT_TYPE,
1392 JSObject::kHeaderSize,
1393 code,
1394 false);
1395 optimized->shared()->DontAdaptArguments();
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001396 SetProperty(holder, key, optimized, NONE);
1397 return optimized;
1398}
1399
1400
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001401static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1402 HandleScope scope;
1403 ASSERT(args.length() == 1);
1404 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1405
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00001406 InstallBuiltin(holder, "pop", Builtins::ArrayPop);
1407 InstallBuiltin(holder, "push", Builtins::ArrayPush);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001408 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1409 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1410 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1411 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001412 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001413
1414 return *holder;
1415}
1416
1417
ager@chromium.org357bf652010-04-12 11:30:10 +00001418static Object* Runtime_GetGlobalReceiver(Arguments args) {
1419 // Returns a real global receiver, not one of builtins object.
1420 Context* global_context = Top::context()->global()->global_context();
1421 return global_context->global()->global_receiver();
1422}
1423
1424
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001425static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1426 HandleScope scope;
1427 ASSERT(args.length() == 4);
1428 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1429 int index = Smi::cast(args[1])->value();
1430 Handle<String> pattern = args.at<String>(2);
1431 Handle<String> flags = args.at<String>(3);
1432
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001433 // Get the RegExp function from the context in the literals array.
1434 // This is the RegExp function from the context in which the
1435 // function was created. We do not use the RegExp function from the
1436 // current global context because this might be the RegExp function
1437 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001438 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001439 Handle<JSFunction>(
1440 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001441 // Compute the regular expression literal.
1442 bool has_pending_exception;
1443 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001444 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1445 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 if (has_pending_exception) {
1447 ASSERT(Top::has_pending_exception());
1448 return Failure::Exception();
1449 }
1450 literals->set(index, *regexp);
1451 return *regexp;
1452}
1453
1454
1455static Object* Runtime_FunctionGetName(Arguments args) {
1456 NoHandleAllocation ha;
1457 ASSERT(args.length() == 1);
1458
1459 CONVERT_CHECKED(JSFunction, f, args[0]);
1460 return f->shared()->name();
1461}
1462
1463
ager@chromium.org236ad962008-09-25 09:45:57 +00001464static Object* Runtime_FunctionSetName(Arguments args) {
1465 NoHandleAllocation ha;
1466 ASSERT(args.length() == 2);
1467
1468 CONVERT_CHECKED(JSFunction, f, args[0]);
1469 CONVERT_CHECKED(String, name, args[1]);
1470 f->shared()->set_name(name);
1471 return Heap::undefined_value();
1472}
1473
1474
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001475static Object* Runtime_FunctionRemovePrototype(Arguments args) {
1476 NoHandleAllocation ha;
1477 ASSERT(args.length() == 1);
1478
1479 CONVERT_CHECKED(JSFunction, f, args[0]);
1480 Object* obj = f->RemovePrototype();
1481 if (obj->IsFailure()) return obj;
1482
1483 return Heap::undefined_value();
1484}
1485
1486
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001487static Object* Runtime_FunctionGetScript(Arguments args) {
1488 HandleScope scope;
1489 ASSERT(args.length() == 1);
1490
1491 CONVERT_CHECKED(JSFunction, fun, args[0]);
1492 Handle<Object> script = Handle<Object>(fun->shared()->script());
1493 if (!script->IsScript()) return Heap::undefined_value();
1494
1495 return *GetScriptWrapper(Handle<Script>::cast(script));
1496}
1497
1498
1499static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 1);
1502
1503 CONVERT_CHECKED(JSFunction, f, args[0]);
1504 return f->shared()->GetSourceCode();
1505}
1506
1507
1508static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1509 NoHandleAllocation ha;
1510 ASSERT(args.length() == 1);
1511
1512 CONVERT_CHECKED(JSFunction, fun, args[0]);
1513 int pos = fun->shared()->start_position();
1514 return Smi::FromInt(pos);
1515}
1516
1517
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001518static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1519 ASSERT(args.length() == 2);
1520
1521 CONVERT_CHECKED(JSFunction, fun, args[0]);
1522 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1523
1524 Code* code = fun->code();
1525 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1526
1527 Address pc = code->address() + offset;
1528 return Smi::FromInt(fun->code()->SourcePosition(pc));
1529}
1530
1531
1532
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001533static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1534 NoHandleAllocation ha;
1535 ASSERT(args.length() == 2);
1536
1537 CONVERT_CHECKED(JSFunction, fun, args[0]);
1538 CONVERT_CHECKED(String, name, args[1]);
1539 fun->SetInstanceClassName(name);
1540 return Heap::undefined_value();
1541}
1542
1543
1544static Object* Runtime_FunctionSetLength(Arguments args) {
1545 NoHandleAllocation ha;
1546 ASSERT(args.length() == 2);
1547
1548 CONVERT_CHECKED(JSFunction, fun, args[0]);
1549 CONVERT_CHECKED(Smi, length, args[1]);
1550 fun->shared()->set_length(length->value());
1551 return length;
1552}
1553
1554
1555static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001556 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001557 ASSERT(args.length() == 2);
1558
1559 CONVERT_CHECKED(JSFunction, fun, args[0]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001560 ASSERT(fun->should_have_prototype());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001561 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1562 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563 return args[0]; // return TOS
1564}
1565
1566
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001567static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1568 NoHandleAllocation ha;
1569 ASSERT(args.length() == 1);
1570
1571 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001572 return f->shared()->IsApiFunction() ? Heap::true_value()
1573 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001574}
1575
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001576static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1577 NoHandleAllocation ha;
1578 ASSERT(args.length() == 1);
1579
1580 CONVERT_CHECKED(JSFunction, f, args[0]);
1581 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1582}
1583
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001584
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001585static Object* Runtime_SetCode(Arguments args) {
1586 HandleScope scope;
1587 ASSERT(args.length() == 2);
1588
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001589 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001590 Handle<Object> code = args.at<Object>(1);
1591
1592 Handle<Context> context(target->context());
1593
1594 if (!code->IsNull()) {
1595 RUNTIME_ASSERT(code->IsJSFunction());
1596 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001597 Handle<SharedFunctionInfo> shared(fun->shared());
1598 SetExpectedNofProperties(target, shared->expected_nof_properties());
1599
1600 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001601 return Failure::Exception();
1602 }
1603 // Set the code, formal parameter count, and the length of the target
1604 // function.
1605 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001606 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001608 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001609 // Set the source code of the target function to undefined.
1610 // SetCode is only used for built-in constructors like String,
1611 // Array, and Object, and some web code
1612 // doesn't like seeing source code for constructors.
1613 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001614 // Clear the optimization hints related to the compiled code as these are no
1615 // longer valid when the code is overwritten.
1616 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001617 context = Handle<Context>(fun->context());
1618
1619 // Make sure we get a fresh copy of the literal vector to avoid
1620 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001621 int number_of_literals = fun->NumberOfLiterals();
1622 Handle<FixedArray> literals =
1623 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001624 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001625 // Insert the object, regexp and array functions in the literals
1626 // array prefix. These are the functions that will be used when
1627 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001628 literals->set(JSFunction::kLiteralGlobalContextIndex,
1629 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001630 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001631 // It's okay to skip the write barrier here because the literals
1632 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001633 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634 }
1635
1636 target->set_context(*context);
1637 return *target;
1638}
1639
1640
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001641static Object* CharFromCode(Object* char_code) {
1642 uint32_t code;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001643 if (char_code->ToArrayIndex(&code)) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001644 if (code <= 0xffff) {
1645 return Heap::LookupSingleCharacterStringFromCode(code);
1646 }
1647 }
1648 return Heap::empty_string();
1649}
1650
1651
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001652static Object* Runtime_StringCharCodeAt(Arguments args) {
1653 NoHandleAllocation ha;
1654 ASSERT(args.length() == 2);
1655
1656 CONVERT_CHECKED(String, subject, args[0]);
1657 Object* index = args[1];
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001658 RUNTIME_ASSERT(index->IsNumber());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001659
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001660 uint32_t i = 0;
1661 if (index->IsSmi()) {
1662 int value = Smi::cast(index)->value();
1663 if (value < 0) return Heap::nan_value();
1664 i = value;
1665 } else {
1666 ASSERT(index->IsHeapNumber());
1667 double value = HeapNumber::cast(index)->value();
1668 i = static_cast<uint32_t>(DoubleToInteger(value));
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001669 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001670
1671 // Flatten the string. If someone wants to get a char at an index
1672 // in a cons string, it is likely that more indices will be
1673 // accessed.
1674 Object* flat = subject->TryFlatten();
1675 if (flat->IsFailure()) return flat;
1676 subject = String::cast(flat);
1677
1678 if (i >= static_cast<uint32_t>(subject->length())) {
1679 return Heap::nan_value();
1680 }
1681
1682 return Smi::FromInt(subject->Get(i));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001683}
1684
1685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686static Object* Runtime_CharFromCode(Arguments args) {
1687 NoHandleAllocation ha;
1688 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001689 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001690}
1691
lrn@chromium.org25156de2010-04-06 13:10:27 +00001692
1693class FixedArrayBuilder {
1694 public:
1695 explicit FixedArrayBuilder(int initial_capacity)
1696 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1697 length_(0) {
1698 // Require a non-zero initial size. Ensures that doubling the size to
1699 // extend the array will work.
1700 ASSERT(initial_capacity > 0);
1701 }
1702
1703 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1704 : array_(backing_store),
1705 length_(0) {
1706 // Require a non-zero initial size. Ensures that doubling the size to
1707 // extend the array will work.
1708 ASSERT(backing_store->length() > 0);
1709 }
1710
1711 bool HasCapacity(int elements) {
1712 int length = array_->length();
1713 int required_length = length_ + elements;
1714 return (length >= required_length);
1715 }
1716
1717 void EnsureCapacity(int elements) {
1718 int length = array_->length();
1719 int required_length = length_ + elements;
1720 if (length < required_length) {
1721 int new_length = length;
1722 do {
1723 new_length *= 2;
1724 } while (new_length < required_length);
1725 Handle<FixedArray> extended_array =
1726 Factory::NewFixedArrayWithHoles(new_length);
1727 array_->CopyTo(0, *extended_array, 0, length_);
1728 array_ = extended_array;
1729 }
1730 }
1731
1732 void Add(Object* value) {
1733 ASSERT(length_ < capacity());
1734 array_->set(length_, value);
1735 length_++;
1736 }
1737
1738 void Add(Smi* value) {
1739 ASSERT(length_ < capacity());
1740 array_->set(length_, value);
1741 length_++;
1742 }
1743
1744 Handle<FixedArray> array() {
1745 return array_;
1746 }
1747
1748 int length() {
1749 return length_;
1750 }
1751
1752 int capacity() {
1753 return array_->length();
1754 }
1755
1756 Handle<JSArray> ToJSArray() {
1757 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1758 result_array->set_length(Smi::FromInt(length_));
1759 return result_array;
1760 }
1761
1762 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1763 target_array->set_elements(*array_);
1764 target_array->set_length(Smi::FromInt(length_));
1765 return target_array;
1766 }
1767
1768 private:
1769 Handle<FixedArray> array_;
1770 int length_;
1771};
1772
1773
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001774// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001775const int kStringBuilderConcatHelperLengthBits = 11;
1776const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001777
1778template <typename schar>
1779static inline void StringBuilderConcatHelper(String*,
1780 schar*,
1781 FixedArray*,
1782 int);
1783
lrn@chromium.org25156de2010-04-06 13:10:27 +00001784typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1785 StringBuilderSubstringLength;
1786typedef BitField<int,
1787 kStringBuilderConcatHelperLengthBits,
1788 kStringBuilderConcatHelperPositionBits>
1789 StringBuilderSubstringPosition;
1790
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001791
1792class ReplacementStringBuilder {
1793 public:
1794 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001795 : array_builder_(estimated_part_count),
1796 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001797 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001798 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001799 // Require a non-zero initial size. Ensures that doubling the size to
1800 // extend the array will work.
1801 ASSERT(estimated_part_count > 0);
1802 }
1803
lrn@chromium.org25156de2010-04-06 13:10:27 +00001804 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1805 int from,
1806 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001807 ASSERT(from >= 0);
1808 int length = to - from;
1809 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001810 if (StringBuilderSubstringLength::is_valid(length) &&
1811 StringBuilderSubstringPosition::is_valid(from)) {
1812 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1813 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001814 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001815 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001816 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001817 builder->Add(Smi::FromInt(-length));
1818 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001819 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001820 }
1821
1822
1823 void EnsureCapacity(int elements) {
1824 array_builder_.EnsureCapacity(elements);
1825 }
1826
1827
1828 void AddSubjectSlice(int from, int to) {
1829 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001830 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001831 }
1832
1833
1834 void AddString(Handle<String> string) {
1835 int length = string->length();
1836 ASSERT(length > 0);
1837 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001838 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001839 is_ascii_ = false;
1840 }
1841 IncrementCharacterCount(length);
1842 }
1843
1844
1845 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001846 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001847 return Factory::empty_string();
1848 }
1849
1850 Handle<String> joined_string;
1851 if (is_ascii_) {
1852 joined_string = NewRawAsciiString(character_count_);
1853 AssertNoAllocation no_alloc;
1854 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1855 char* char_buffer = seq->GetChars();
1856 StringBuilderConcatHelper(*subject_,
1857 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001858 *array_builder_.array(),
1859 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001860 } else {
1861 // Non-ASCII.
1862 joined_string = NewRawTwoByteString(character_count_);
1863 AssertNoAllocation no_alloc;
1864 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1865 uc16* char_buffer = seq->GetChars();
1866 StringBuilderConcatHelper(*subject_,
1867 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001868 *array_builder_.array(),
1869 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001870 }
1871 return joined_string;
1872 }
1873
1874
1875 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001876 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001877 V8::FatalProcessOutOfMemory("String.replace result too large.");
1878 }
1879 character_count_ += by;
1880 }
1881
lrn@chromium.org25156de2010-04-06 13:10:27 +00001882 Handle<JSArray> GetParts() {
1883 Handle<JSArray> result =
1884 Factory::NewJSArrayWithElements(array_builder_.array());
1885 result->set_length(Smi::FromInt(array_builder_.length()));
1886 return result;
1887 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001888
lrn@chromium.org25156de2010-04-06 13:10:27 +00001889 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001890 Handle<String> NewRawAsciiString(int size) {
1891 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1892 }
1893
1894
1895 Handle<String> NewRawTwoByteString(int size) {
1896 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1897 }
1898
1899
1900 void AddElement(Object* element) {
1901 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001902 ASSERT(array_builder_.capacity() > array_builder_.length());
1903 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001904 }
1905
lrn@chromium.org25156de2010-04-06 13:10:27 +00001906 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001907 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001908 int character_count_;
1909 bool is_ascii_;
1910};
1911
1912
1913class CompiledReplacement {
1914 public:
1915 CompiledReplacement()
1916 : parts_(1), replacement_substrings_(0) {}
1917
1918 void Compile(Handle<String> replacement,
1919 int capture_count,
1920 int subject_length);
1921
1922 void Apply(ReplacementStringBuilder* builder,
1923 int match_from,
1924 int match_to,
1925 Handle<JSArray> last_match_info);
1926
1927 // Number of distinct parts of the replacement pattern.
1928 int parts() {
1929 return parts_.length();
1930 }
1931 private:
1932 enum PartType {
1933 SUBJECT_PREFIX = 1,
1934 SUBJECT_SUFFIX,
1935 SUBJECT_CAPTURE,
1936 REPLACEMENT_SUBSTRING,
1937 REPLACEMENT_STRING,
1938
1939 NUMBER_OF_PART_TYPES
1940 };
1941
1942 struct ReplacementPart {
1943 static inline ReplacementPart SubjectMatch() {
1944 return ReplacementPart(SUBJECT_CAPTURE, 0);
1945 }
1946 static inline ReplacementPart SubjectCapture(int capture_index) {
1947 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1948 }
1949 static inline ReplacementPart SubjectPrefix() {
1950 return ReplacementPart(SUBJECT_PREFIX, 0);
1951 }
1952 static inline ReplacementPart SubjectSuffix(int subject_length) {
1953 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1954 }
1955 static inline ReplacementPart ReplacementString() {
1956 return ReplacementPart(REPLACEMENT_STRING, 0);
1957 }
1958 static inline ReplacementPart ReplacementSubString(int from, int to) {
1959 ASSERT(from >= 0);
1960 ASSERT(to > from);
1961 return ReplacementPart(-from, to);
1962 }
1963
1964 // If tag <= 0 then it is the negation of a start index of a substring of
1965 // the replacement pattern, otherwise it's a value from PartType.
1966 ReplacementPart(int tag, int data)
1967 : tag(tag), data(data) {
1968 // Must be non-positive or a PartType value.
1969 ASSERT(tag < NUMBER_OF_PART_TYPES);
1970 }
1971 // Either a value of PartType or a non-positive number that is
1972 // the negation of an index into the replacement string.
1973 int tag;
1974 // The data value's interpretation depends on the value of tag:
1975 // tag == SUBJECT_PREFIX ||
1976 // tag == SUBJECT_SUFFIX: data is unused.
1977 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1978 // tag == REPLACEMENT_SUBSTRING ||
1979 // tag == REPLACEMENT_STRING: data is index into array of substrings
1980 // of the replacement string.
1981 // tag <= 0: Temporary representation of the substring of the replacement
1982 // string ranging over -tag .. data.
1983 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1984 // substring objects.
1985 int data;
1986 };
1987
1988 template<typename Char>
1989 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1990 Vector<Char> characters,
1991 int capture_count,
1992 int subject_length) {
1993 int length = characters.length();
1994 int last = 0;
1995 for (int i = 0; i < length; i++) {
1996 Char c = characters[i];
1997 if (c == '$') {
1998 int next_index = i + 1;
1999 if (next_index == length) { // No next character!
2000 break;
2001 }
2002 Char c2 = characters[next_index];
2003 switch (c2) {
2004 case '$':
2005 if (i > last) {
2006 // There is a substring before. Include the first "$".
2007 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
2008 last = next_index + 1; // Continue after the second "$".
2009 } else {
2010 // Let the next substring start with the second "$".
2011 last = next_index;
2012 }
2013 i = next_index;
2014 break;
2015 case '`':
2016 if (i > last) {
2017 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2018 }
2019 parts->Add(ReplacementPart::SubjectPrefix());
2020 i = next_index;
2021 last = i + 1;
2022 break;
2023 case '\'':
2024 if (i > last) {
2025 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2026 }
2027 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2028 i = next_index;
2029 last = i + 1;
2030 break;
2031 case '&':
2032 if (i > last) {
2033 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2034 }
2035 parts->Add(ReplacementPart::SubjectMatch());
2036 i = next_index;
2037 last = i + 1;
2038 break;
2039 case '0':
2040 case '1':
2041 case '2':
2042 case '3':
2043 case '4':
2044 case '5':
2045 case '6':
2046 case '7':
2047 case '8':
2048 case '9': {
2049 int capture_ref = c2 - '0';
2050 if (capture_ref > capture_count) {
2051 i = next_index;
2052 continue;
2053 }
2054 int second_digit_index = next_index + 1;
2055 if (second_digit_index < length) {
2056 // Peek ahead to see if we have two digits.
2057 Char c3 = characters[second_digit_index];
2058 if ('0' <= c3 && c3 <= '9') { // Double digits.
2059 int double_digit_ref = capture_ref * 10 + c3 - '0';
2060 if (double_digit_ref <= capture_count) {
2061 next_index = second_digit_index;
2062 capture_ref = double_digit_ref;
2063 }
2064 }
2065 }
2066 if (capture_ref > 0) {
2067 if (i > last) {
2068 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2069 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002070 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002071 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2072 last = next_index + 1;
2073 }
2074 i = next_index;
2075 break;
2076 }
2077 default:
2078 i = next_index;
2079 break;
2080 }
2081 }
2082 }
2083 if (length > last) {
2084 if (last == 0) {
2085 parts->Add(ReplacementPart::ReplacementString());
2086 } else {
2087 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2088 }
2089 }
2090 }
2091
2092 ZoneList<ReplacementPart> parts_;
2093 ZoneList<Handle<String> > replacement_substrings_;
2094};
2095
2096
2097void CompiledReplacement::Compile(Handle<String> replacement,
2098 int capture_count,
2099 int subject_length) {
2100 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002101 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002102 AssertNoAllocation no_alloc;
2103 ParseReplacementPattern(&parts_,
2104 replacement->ToAsciiVector(),
2105 capture_count,
2106 subject_length);
2107 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002108 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002109 AssertNoAllocation no_alloc;
2110
2111 ParseReplacementPattern(&parts_,
2112 replacement->ToUC16Vector(),
2113 capture_count,
2114 subject_length);
2115 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002116 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002117 int substring_index = 0;
2118 for (int i = 0, n = parts_.length(); i < n; i++) {
2119 int tag = parts_[i].tag;
2120 if (tag <= 0) { // A replacement string slice.
2121 int from = -tag;
2122 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002123 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002124 parts_[i].tag = REPLACEMENT_SUBSTRING;
2125 parts_[i].data = substring_index;
2126 substring_index++;
2127 } else if (tag == REPLACEMENT_STRING) {
2128 replacement_substrings_.Add(replacement);
2129 parts_[i].data = substring_index;
2130 substring_index++;
2131 }
2132 }
2133}
2134
2135
2136void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2137 int match_from,
2138 int match_to,
2139 Handle<JSArray> last_match_info) {
2140 for (int i = 0, n = parts_.length(); i < n; i++) {
2141 ReplacementPart part = parts_[i];
2142 switch (part.tag) {
2143 case SUBJECT_PREFIX:
2144 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2145 break;
2146 case SUBJECT_SUFFIX: {
2147 int subject_length = part.data;
2148 if (match_to < subject_length) {
2149 builder->AddSubjectSlice(match_to, subject_length);
2150 }
2151 break;
2152 }
2153 case SUBJECT_CAPTURE: {
2154 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002155 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002156 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2157 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2158 if (from >= 0 && to > from) {
2159 builder->AddSubjectSlice(from, to);
2160 }
2161 break;
2162 }
2163 case REPLACEMENT_SUBSTRING:
2164 case REPLACEMENT_STRING:
2165 builder->AddString(replacement_substrings_[part.data]);
2166 break;
2167 default:
2168 UNREACHABLE();
2169 }
2170 }
2171}
2172
2173
2174
2175static Object* StringReplaceRegExpWithString(String* subject,
2176 JSRegExp* regexp,
2177 String* replacement,
2178 JSArray* last_match_info) {
2179 ASSERT(subject->IsFlat());
2180 ASSERT(replacement->IsFlat());
2181
2182 HandleScope handles;
2183
2184 int length = subject->length();
2185 Handle<String> subject_handle(subject);
2186 Handle<JSRegExp> regexp_handle(regexp);
2187 Handle<String> replacement_handle(replacement);
2188 Handle<JSArray> last_match_info_handle(last_match_info);
2189 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2190 subject_handle,
2191 0,
2192 last_match_info_handle);
2193 if (match.is_null()) {
2194 return Failure::Exception();
2195 }
2196 if (match->IsNull()) {
2197 return *subject_handle;
2198 }
2199
2200 int capture_count = regexp_handle->CaptureCount();
2201
2202 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002203 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002204 CompiledReplacement compiled_replacement;
2205 compiled_replacement.Compile(replacement_handle,
2206 capture_count,
2207 length);
2208
2209 bool is_global = regexp_handle->GetFlags().is_global();
2210
2211 // Guessing the number of parts that the final result string is built
2212 // from. Global regexps can match any number of times, so we guess
2213 // conservatively.
2214 int expected_parts =
2215 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2216 ReplacementStringBuilder builder(subject_handle, expected_parts);
2217
2218 // Index of end of last match.
2219 int prev = 0;
2220
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002221 // Number of parts added by compiled replacement plus preceeding
2222 // string and possibly suffix after last match. It is possible for
2223 // all components to use two elements when encoded as two smis.
2224 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002225 bool matched = true;
2226 do {
2227 ASSERT(last_match_info_handle->HasFastElements());
2228 // Increase the capacity of the builder before entering local handle-scope,
2229 // so its internal buffer can safely allocate a new handle if it grows.
2230 builder.EnsureCapacity(parts_added_per_loop);
2231
2232 HandleScope loop_scope;
2233 int start, end;
2234 {
2235 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002236 FixedArray* match_info_array =
2237 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002238
2239 ASSERT_EQ(capture_count * 2 + 2,
2240 RegExpImpl::GetLastCaptureCount(match_info_array));
2241 start = RegExpImpl::GetCapture(match_info_array, 0);
2242 end = RegExpImpl::GetCapture(match_info_array, 1);
2243 }
2244
2245 if (prev < start) {
2246 builder.AddSubjectSlice(prev, start);
2247 }
2248 compiled_replacement.Apply(&builder,
2249 start,
2250 end,
2251 last_match_info_handle);
2252 prev = end;
2253
2254 // Only continue checking for global regexps.
2255 if (!is_global) break;
2256
2257 // Continue from where the match ended, unless it was an empty match.
2258 int next = end;
2259 if (start == end) {
2260 next = end + 1;
2261 if (next > length) break;
2262 }
2263
2264 match = RegExpImpl::Exec(regexp_handle,
2265 subject_handle,
2266 next,
2267 last_match_info_handle);
2268 if (match.is_null()) {
2269 return Failure::Exception();
2270 }
2271 matched = !match->IsNull();
2272 } while (matched);
2273
2274 if (prev < length) {
2275 builder.AddSubjectSlice(prev, length);
2276 }
2277
2278 return *(builder.ToString());
2279}
2280
2281
2282static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2283 ASSERT(args.length() == 4);
2284
2285 CONVERT_CHECKED(String, subject, args[0]);
2286 if (!subject->IsFlat()) {
2287 Object* flat_subject = subject->TryFlatten();
2288 if (flat_subject->IsFailure()) {
2289 return flat_subject;
2290 }
2291 subject = String::cast(flat_subject);
2292 }
2293
2294 CONVERT_CHECKED(String, replacement, args[2]);
2295 if (!replacement->IsFlat()) {
2296 Object* flat_replacement = replacement->TryFlatten();
2297 if (flat_replacement->IsFailure()) {
2298 return flat_replacement;
2299 }
2300 replacement = String::cast(flat_replacement);
2301 }
2302
2303 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2304 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2305
2306 ASSERT(last_match_info->HasFastElements());
2307
2308 return StringReplaceRegExpWithString(subject,
2309 regexp,
2310 replacement,
2311 last_match_info);
2312}
2313
2314
ager@chromium.org7c537e22008-10-16 08:43:32 +00002315// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2316// limit, we can fix the size of tables.
2317static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002318// Reduce alphabet to this size.
2319static const int kBMAlphabetSize = 0x100;
2320// For patterns below this length, the skip length of Boyer-Moore is too short
2321// to compensate for the algorithmic overhead compared to simple brute force.
2322static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002323
ager@chromium.org7c537e22008-10-16 08:43:32 +00002324// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2325// shift. Only allows the last kBMMaxShift characters of the needle
2326// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002327class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002328 public:
2329 BMGoodSuffixBuffers() {}
2330 inline void init(int needle_length) {
2331 ASSERT(needle_length > 1);
2332 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2333 int len = needle_length - start;
2334 biased_suffixes_ = suffixes_ - start;
2335 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2336 for (int i = 0; i <= len; i++) {
2337 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002338 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002339 }
2340 inline int& suffix(int index) {
2341 ASSERT(biased_suffixes_ + index >= suffixes_);
2342 return biased_suffixes_[index];
2343 }
2344 inline int& shift(int index) {
2345 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2346 return biased_good_suffix_shift_[index];
2347 }
2348 private:
2349 int suffixes_[kBMMaxShift + 1];
2350 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002351 int* biased_suffixes_;
2352 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002353 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2354};
2355
2356// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002357static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002358static BMGoodSuffixBuffers bmgs_buffers;
2359
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002360// State of the string match tables.
2361// SIMPLE: No usable content in the buffers.
2362// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2363// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2364// Whenever starting with a new needle, one should call InitializeStringSearch
2365// to determine which search strategy to use, and in the case of a long-needle
2366// strategy, the call also initializes the algorithm to SIMPLE.
2367enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2368static StringSearchAlgorithm algorithm;
2369
2370
ager@chromium.org7c537e22008-10-16 08:43:32 +00002371// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002372template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002373static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2374 // Only preprocess at most kBMMaxShift last characters of pattern.
2375 int start = pattern.length() < kBMMaxShift ? 0
2376 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002377 // Run forwards to populate bad_char_table, so that *last* instance
2378 // of character equivalence class is the one registered.
2379 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002380 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2381 : kBMAlphabetSize;
2382 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002383 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002384 } else {
2385 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002386 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002387 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002388 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002389 for (int i = start; i < pattern.length() - 1; i++) {
2390 pchar c = pattern[i];
2391 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002392 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002393 }
2394}
2395
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002396
ager@chromium.org7c537e22008-10-16 08:43:32 +00002397template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002398static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002399 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002400 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002401 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002402 // Compute Good Suffix tables.
2403 bmgs_buffers.init(m);
2404
2405 bmgs_buffers.shift(m-1) = 1;
2406 bmgs_buffers.suffix(m) = m + 1;
2407 pchar last_char = pattern[m - 1];
2408 int suffix = m + 1;
2409 for (int i = m; i > start;) {
2410 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2411 if (bmgs_buffers.shift(suffix) == len) {
2412 bmgs_buffers.shift(suffix) = suffix - i;
2413 }
2414 suffix = bmgs_buffers.suffix(suffix);
2415 }
2416 i--;
2417 suffix--;
2418 bmgs_buffers.suffix(i) = suffix;
2419 if (suffix == m) {
2420 // No suffix to extend, so we check against last_char only.
2421 while (i > start && pattern[i - 1] != last_char) {
2422 if (bmgs_buffers.shift(m) == len) {
2423 bmgs_buffers.shift(m) = m - i;
2424 }
2425 i--;
2426 bmgs_buffers.suffix(i) = m;
2427 }
2428 if (i > start) {
2429 i--;
2430 suffix--;
2431 bmgs_buffers.suffix(i) = suffix;
2432 }
2433 }
2434 }
2435 if (suffix < m) {
2436 for (int i = start; i <= m; i++) {
2437 if (bmgs_buffers.shift(i) == len) {
2438 bmgs_buffers.shift(i) = suffix - start;
2439 }
2440 if (i == suffix) {
2441 suffix = bmgs_buffers.suffix(suffix);
2442 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002443 }
2444 }
2445}
2446
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002447
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002448template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002449static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002450 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002451 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002452 }
2453 if (sizeof(pchar) == 1) {
2454 if (char_code > String::kMaxAsciiCharCode) {
2455 return -1;
2456 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002457 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002458 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002459 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002460}
2461
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002462
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002463// Restricted simplified Boyer-Moore string matching.
2464// Uses only the bad-shift table of Boyer-Moore and only uses it
2465// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002466template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002467static int BoyerMooreHorspool(Vector<const schar> subject,
2468 Vector<const pchar> pattern,
2469 int start_index,
2470 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002471 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002473 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002475 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002476
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002477 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002479 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002480 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002481 // Perform search
2482 for (idx = start_index; idx <= n - m;) {
2483 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002484 int c;
2485 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002486 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002487 int shift = j - bc_occ;
2488 idx += shift;
2489 badness += 1 - shift; // at most zero, so badness cannot increase.
2490 if (idx > n - m) {
2491 *complete = true;
2492 return -1;
2493 }
2494 }
2495 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002496 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002497 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002498 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002499 return idx;
2500 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002501 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002502 // Badness increases by the number of characters we have
2503 // checked, and decreases by the number of characters we
2504 // can skip by shifting. It's a measure of how we are doing
2505 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002506 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002507 if (badness > 0) {
2508 *complete = false;
2509 return idx;
2510 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002511 }
2512 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002513 *complete = true;
2514 return -1;
2515}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002517
2518template <typename schar, typename pchar>
2519static int BoyerMooreIndexOf(Vector<const schar> subject,
2520 Vector<const pchar> pattern,
2521 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002522 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523 int n = subject.length();
2524 int m = pattern.length();
2525 // Only preprocess at most kBMMaxShift last characters of pattern.
2526 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2527
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002528 pchar last_char = pattern[m - 1];
2529 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002530 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002531 int j = m - 1;
2532 schar c;
2533 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002534 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002535 idx += shift;
2536 if (idx > n - m) {
2537 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002538 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002539 }
2540 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2541 if (j < 0) {
2542 return idx;
2543 } else if (j < start) {
2544 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002545 // Fall back on BMH shift.
2546 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002547 } else {
2548 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002549 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002550 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002551 if (gs_shift > shift) {
2552 shift = gs_shift;
2553 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002554 idx += shift;
2555 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002556 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002557
2558 return -1;
2559}
2560
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002561
2562template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002563static inline int SingleCharIndexOf(Vector<const schar> string,
2564 schar pattern_char,
2565 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002566 if (sizeof(schar) == 1) {
2567 const schar* pos = reinterpret_cast<const schar*>(
2568 memchr(string.start() + start_index,
2569 pattern_char,
2570 string.length() - start_index));
2571 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002572 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002573 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002574 for (int i = start_index, n = string.length(); i < n; i++) {
2575 if (pattern_char == string[i]) {
2576 return i;
2577 }
2578 }
2579 return -1;
2580}
2581
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002582
2583template <typename schar>
2584static int SingleCharLastIndexOf(Vector<const schar> string,
2585 schar pattern_char,
2586 int start_index) {
2587 for (int i = start_index; i >= 0; i--) {
2588 if (pattern_char == string[i]) {
2589 return i;
2590 }
2591 }
2592 return -1;
2593}
2594
2595
ager@chromium.org7c537e22008-10-16 08:43:32 +00002596// Trivial string search for shorter strings.
2597// On return, if "complete" is set to true, the return value is the
2598// final result of searching for the patter in the subject.
2599// If "complete" is set to false, the return value is the index where
2600// further checking should start, i.e., it's guaranteed that the pattern
2601// does not occur at a position prior to the returned index.
2602template <typename pchar, typename schar>
2603static int SimpleIndexOf(Vector<const schar> subject,
2604 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002605 int idx,
2606 bool* complete) {
2607 // Badness is a count of how much work we have done. When we have
2608 // done enough work we decide it's probably worth switching to a better
2609 // algorithm.
2610 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002611
ager@chromium.org7c537e22008-10-16 08:43:32 +00002612 // We know our pattern is at least 2 characters, we cache the first so
2613 // the common case of the first character not matching is faster.
2614 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2616 badness++;
2617 if (badness > 0) {
2618 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002619 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002621 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2622 const schar* pos = reinterpret_cast<const schar*>(
2623 memchr(subject.start() + i,
2624 pattern_first_char,
2625 n - i + 1));
2626 if (pos == NULL) {
2627 *complete = true;
2628 return -1;
2629 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002630 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002631 } else {
2632 if (subject[i] != pattern_first_char) continue;
2633 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002634 int j = 1;
2635 do {
2636 if (pattern[j] != subject[i+j]) {
2637 break;
2638 }
2639 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002640 } while (j < pattern.length());
2641 if (j == pattern.length()) {
2642 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002643 return i;
2644 }
2645 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002646 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002648 return -1;
2649}
2650
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651// Simple indexOf that never bails out. For short patterns only.
2652template <typename pchar, typename schar>
2653static int SimpleIndexOf(Vector<const schar> subject,
2654 Vector<const pchar> pattern,
2655 int idx) {
2656 pchar pattern_first_char = pattern[0];
2657 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002658 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2659 const schar* pos = reinterpret_cast<const schar*>(
2660 memchr(subject.start() + i,
2661 pattern_first_char,
2662 n - i + 1));
2663 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002664 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002665 } else {
2666 if (subject[i] != pattern_first_char) continue;
2667 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002668 int j = 1;
2669 do {
2670 if (pattern[j] != subject[i+j]) {
2671 break;
2672 }
2673 j++;
2674 } while (j < pattern.length());
2675 if (j == pattern.length()) {
2676 return i;
2677 }
2678 }
2679 return -1;
2680}
2681
2682
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002683// Strategy for searching for a string in another string.
2684enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002685
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002686
2687template <typename pchar>
2688static inline StringSearchStrategy InitializeStringSearch(
2689 Vector<const pchar> pat, bool ascii_subject) {
2690 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002691 // We have an ASCII haystack and a non-ASCII needle. Check if there
2692 // really is a non-ASCII character in the needle and bail out if there
2693 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002694 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002695 for (int i = 0; i < pat.length(); i++) {
2696 uc16 c = pat[i];
2697 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002698 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002699 }
2700 }
2701 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002702 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002703 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002704 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002705 algorithm = SIMPLE_SEARCH;
2706 return SEARCH_LONG;
2707}
2708
2709
2710// Dispatch long needle searches to different algorithms.
2711template <typename schar, typename pchar>
2712static int ComplexIndexOf(Vector<const schar> sub,
2713 Vector<const pchar> pat,
2714 int start_index) {
2715 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002716 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002717 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002718 int idx = start_index;
2719 switch (algorithm) {
2720 case SIMPLE_SEARCH:
2721 idx = SimpleIndexOf(sub, pat, idx, &complete);
2722 if (complete) return idx;
2723 BoyerMoorePopulateBadCharTable(pat);
2724 algorithm = BOYER_MOORE_HORSPOOL;
2725 // FALLTHROUGH.
2726 case BOYER_MOORE_HORSPOOL:
2727 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2728 if (complete) return idx;
2729 // Build the Good Suffix table and continue searching.
2730 BoyerMoorePopulateGoodSuffixTable(pat);
2731 algorithm = BOYER_MOORE;
2732 // FALLTHROUGH.
2733 case BOYER_MOORE:
2734 return BoyerMooreIndexOf(sub, pat, idx);
2735 }
2736 UNREACHABLE();
2737 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002738}
2739
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002740
2741// Dispatch to different search strategies for a single search.
2742// If searching multiple times on the same needle, the search
2743// strategy should only be computed once and then dispatch to different
2744// loops.
2745template <typename schar, typename pchar>
2746static int StringSearch(Vector<const schar> sub,
2747 Vector<const pchar> pat,
2748 int start_index) {
2749 bool ascii_subject = (sizeof(schar) == 1);
2750 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2751 switch (strategy) {
2752 case SEARCH_FAIL: return -1;
2753 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2754 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2755 }
2756 UNREACHABLE();
2757 return -1;
2758}
2759
2760
ager@chromium.org7c537e22008-10-16 08:43:32 +00002761// Perform string match of pattern on subject, starting at start index.
2762// Caller must ensure that 0 <= start_index <= sub->length(),
2763// and should check that pat->length() + start_index <= sub->length()
2764int Runtime::StringMatch(Handle<String> sub,
2765 Handle<String> pat,
2766 int start_index) {
2767 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002768 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002769
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002770 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002771 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002772
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002773 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002774 if (start_index + pattern_length > subject_length) return -1;
2775
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002776 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002777 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002778 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002779
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002781 // character patterns linear search is necessary, so any smart
2782 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002783 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002784 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002785 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002786 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002787 if (pchar > String::kMaxAsciiCharCode) {
2788 return -1;
2789 }
2790 Vector<const char> ascii_vector =
2791 sub->ToAsciiVector().SubVector(start_index, subject_length);
2792 const void* pos = memchr(ascii_vector.start(),
2793 static_cast<const char>(pchar),
2794 static_cast<size_t>(ascii_vector.length()));
2795 if (pos == NULL) {
2796 return -1;
2797 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002798 return static_cast<int>(reinterpret_cast<const char*>(pos)
2799 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002800 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002801 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002802 }
2803
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002804 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002805 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002806 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002807
ager@chromium.org7c537e22008-10-16 08:43:32 +00002808 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2809 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002810 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002811 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002812 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002813 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002814 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002815 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002816 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002817 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002818 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002819 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002820 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002821 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002822}
2823
2824
2825static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002826 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002827 ASSERT(args.length() == 3);
2828
ager@chromium.org7c537e22008-10-16 08:43:32 +00002829 CONVERT_ARG_CHECKED(String, sub, 0);
2830 CONVERT_ARG_CHECKED(String, pat, 1);
2831
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002832 Object* index = args[2];
2833 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002834 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002835
ager@chromium.org870a0b62008-11-04 11:43:05 +00002836 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002837 int position = Runtime::StringMatch(sub, pat, start_index);
2838 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002839}
2840
2841
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002842template <typename schar, typename pchar>
2843static int StringMatchBackwards(Vector<const schar> sub,
2844 Vector<const pchar> pat,
2845 int idx) {
2846 ASSERT(pat.length() >= 1);
2847 ASSERT(idx + pat.length() <= sub.length());
2848
2849 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2850 for (int i = 0; i < pat.length(); i++) {
2851 uc16 c = pat[i];
2852 if (c > String::kMaxAsciiCharCode) {
2853 return -1;
2854 }
2855 }
2856 }
2857
2858 pchar pattern_first_char = pat[0];
2859 for (int i = idx; i >= 0; i--) {
2860 if (sub[i] != pattern_first_char) continue;
2861 int j = 1;
2862 while (j < pat.length()) {
2863 if (pat[j] != sub[i+j]) {
2864 break;
2865 }
2866 j++;
2867 }
2868 if (j == pat.length()) {
2869 return i;
2870 }
2871 }
2872 return -1;
2873}
2874
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002876 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002877 ASSERT(args.length() == 3);
2878
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002879 CONVERT_ARG_CHECKED(String, sub, 0);
2880 CONVERT_ARG_CHECKED(String, pat, 1);
2881
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002882 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002883 uint32_t start_index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002884 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002885
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002886 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002887 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002888
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002889 if (start_index + pat_length > sub_length) {
2890 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002891 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002892
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002893 if (pat_length == 0) {
2894 return Smi::FromInt(start_index);
2895 }
2896
2897 if (!sub->IsFlat()) {
2898 FlattenString(sub);
2899 }
2900
2901 if (pat_length == 1) {
2902 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2903 if (sub->IsAsciiRepresentation()) {
2904 uc16 pchar = pat->Get(0);
2905 if (pchar > String::kMaxAsciiCharCode) {
2906 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002908 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2909 static_cast<char>(pat->Get(0)),
2910 start_index));
2911 } else {
2912 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2913 pat->Get(0),
2914 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002916 }
2917
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002918 if (!pat->IsFlat()) {
2919 FlattenString(pat);
2920 }
2921
2922 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2923
2924 int position = -1;
2925
2926 if (pat->IsAsciiRepresentation()) {
2927 Vector<const char> pat_vector = pat->ToAsciiVector();
2928 if (sub->IsAsciiRepresentation()) {
2929 position = StringMatchBackwards(sub->ToAsciiVector(),
2930 pat_vector,
2931 start_index);
2932 } else {
2933 position = StringMatchBackwards(sub->ToUC16Vector(),
2934 pat_vector,
2935 start_index);
2936 }
2937 } else {
2938 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2939 if (sub->IsAsciiRepresentation()) {
2940 position = StringMatchBackwards(sub->ToAsciiVector(),
2941 pat_vector,
2942 start_index);
2943 } else {
2944 position = StringMatchBackwards(sub->ToUC16Vector(),
2945 pat_vector,
2946 start_index);
2947 }
2948 }
2949
2950 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002951}
2952
2953
2954static Object* Runtime_StringLocaleCompare(Arguments args) {
2955 NoHandleAllocation ha;
2956 ASSERT(args.length() == 2);
2957
2958 CONVERT_CHECKED(String, str1, args[0]);
2959 CONVERT_CHECKED(String, str2, args[1]);
2960
2961 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002962 int str1_length = str1->length();
2963 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002964
2965 // Decide trivial cases without flattening.
2966 if (str1_length == 0) {
2967 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2968 return Smi::FromInt(-str2_length);
2969 } else {
2970 if (str2_length == 0) return Smi::FromInt(str1_length);
2971 }
2972
2973 int end = str1_length < str2_length ? str1_length : str2_length;
2974
2975 // No need to flatten if we are going to find the answer on the first
2976 // character. At this point we know there is at least one character
2977 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002978 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002979 if (d != 0) return Smi::FromInt(d);
2980
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002981 str1->TryFlatten();
2982 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002983
2984 static StringInputBuffer buf1;
2985 static StringInputBuffer buf2;
2986
2987 buf1.Reset(str1);
2988 buf2.Reset(str2);
2989
2990 for (int i = 0; i < end; i++) {
2991 uint16_t char1 = buf1.GetNext();
2992 uint16_t char2 = buf2.GetNext();
2993 if (char1 != char2) return Smi::FromInt(char1 - char2);
2994 }
2995
2996 return Smi::FromInt(str1_length - str2_length);
2997}
2998
2999
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003000static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003001 NoHandleAllocation ha;
3002 ASSERT(args.length() == 3);
3003
3004 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00003005 Object* from = args[1];
3006 Object* to = args[2];
3007 int start, end;
3008 // We have a fast integer-only case here to avoid a conversion to double in
3009 // the common case where from and to are Smis.
3010 if (from->IsSmi() && to->IsSmi()) {
3011 start = Smi::cast(from)->value();
3012 end = Smi::cast(to)->value();
3013 } else {
3014 CONVERT_DOUBLE_CHECKED(from_number, from);
3015 CONVERT_DOUBLE_CHECKED(to_number, to);
3016 start = FastD2I(from_number);
3017 end = FastD2I(to_number);
3018 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003019 RUNTIME_ASSERT(end >= start);
3020 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003021 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003022 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003023 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003024}
3025
3026
ager@chromium.org41826e72009-03-30 13:30:57 +00003027static Object* Runtime_StringMatch(Arguments args) {
3028 ASSERT_EQ(3, args.length());
3029
3030 CONVERT_ARG_CHECKED(String, subject, 0);
3031 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3032 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3033 HandleScope handles;
3034
3035 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3036
3037 if (match.is_null()) {
3038 return Failure::Exception();
3039 }
3040 if (match->IsNull()) {
3041 return Heap::null_value();
3042 }
3043 int length = subject->length();
3044
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003045 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003046 ZoneList<int> offsets(8);
3047 do {
3048 int start;
3049 int end;
3050 {
3051 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003052 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003053 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3054 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3055 }
3056 offsets.Add(start);
3057 offsets.Add(end);
3058 int index = start < end ? end : end + 1;
3059 if (index > length) break;
3060 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3061 if (match.is_null()) {
3062 return Failure::Exception();
3063 }
3064 } while (!match->IsNull());
3065 int matches = offsets.length() / 2;
3066 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3067 for (int i = 0; i < matches ; i++) {
3068 int from = offsets.at(i * 2);
3069 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003070 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003071 }
3072 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3073 result->set_length(Smi::FromInt(matches));
3074 return *result;
3075}
3076
3077
lrn@chromium.org25156de2010-04-06 13:10:27 +00003078// Two smis before and after the match, for very long strings.
3079const int kMaxBuilderEntriesPerRegExpMatch = 5;
3080
3081
3082static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3083 Handle<JSArray> last_match_info,
3084 int match_start,
3085 int match_end) {
3086 // Fill last_match_info with a single capture.
3087 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3088 AssertNoAllocation no_gc;
3089 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3090 RegExpImpl::SetLastCaptureCount(elements, 2);
3091 RegExpImpl::SetLastInput(elements, *subject);
3092 RegExpImpl::SetLastSubject(elements, *subject);
3093 RegExpImpl::SetCapture(elements, 0, match_start);
3094 RegExpImpl::SetCapture(elements, 1, match_end);
3095}
3096
3097
3098template <typename schar>
3099static bool SearchCharMultiple(Vector<schar> subject,
3100 String* pattern,
3101 schar pattern_char,
3102 FixedArrayBuilder* builder,
3103 int* match_pos) {
3104 // Position of last match.
3105 int pos = *match_pos;
3106 int subject_length = subject.length();
3107 while (pos < subject_length) {
3108 int match_end = pos + 1;
3109 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3110 *match_pos = pos;
3111 return false;
3112 }
3113 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3114 if (new_pos >= 0) {
3115 // Match has been found.
3116 if (new_pos > match_end) {
3117 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3118 }
3119 pos = new_pos;
3120 builder->Add(pattern);
3121 } else {
3122 break;
3123 }
3124 }
3125 if (pos + 1 < subject_length) {
3126 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3127 }
3128 *match_pos = pos;
3129 return true;
3130}
3131
3132
3133static bool SearchCharMultiple(Handle<String> subject,
3134 Handle<String> pattern,
3135 Handle<JSArray> last_match_info,
3136 FixedArrayBuilder* builder) {
3137 ASSERT(subject->IsFlat());
3138 ASSERT_EQ(1, pattern->length());
3139 uc16 pattern_char = pattern->Get(0);
3140 // Treating position before first as initial "previous match position".
3141 int match_pos = -1;
3142
3143 for (;;) { // Break when search complete.
3144 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3145 AssertNoAllocation no_gc;
3146 if (subject->IsAsciiRepresentation()) {
3147 if (pattern_char > String::kMaxAsciiCharCode) {
3148 break;
3149 }
3150 Vector<const char> subject_vector = subject->ToAsciiVector();
3151 char pattern_ascii_char = static_cast<char>(pattern_char);
3152 bool complete = SearchCharMultiple<const char>(subject_vector,
3153 *pattern,
3154 pattern_ascii_char,
3155 builder,
3156 &match_pos);
3157 if (complete) break;
3158 } else {
3159 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3160 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3161 *pattern,
3162 pattern_char,
3163 builder,
3164 &match_pos);
3165 if (complete) break;
3166 }
3167 }
3168
3169 if (match_pos >= 0) {
3170 SetLastMatchInfoNoCaptures(subject,
3171 last_match_info,
3172 match_pos,
3173 match_pos + 1);
3174 return true;
3175 }
3176 return false; // No matches at all.
3177}
3178
3179
3180template <typename schar, typename pchar>
3181static bool SearchStringMultiple(Vector<schar> subject,
3182 String* pattern,
3183 Vector<pchar> pattern_string,
3184 FixedArrayBuilder* builder,
3185 int* match_pos) {
3186 int pos = *match_pos;
3187 int subject_length = subject.length();
3188 int pattern_length = pattern_string.length();
3189 int max_search_start = subject_length - pattern_length;
3190 bool is_ascii = (sizeof(schar) == 1);
3191 StringSearchStrategy strategy =
3192 InitializeStringSearch(pattern_string, is_ascii);
3193 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003194 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003195 case SEARCH_SHORT:
3196 while (pos <= max_search_start) {
3197 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3198 *match_pos = pos;
3199 return false;
3200 }
3201 // Position of end of previous match.
3202 int match_end = pos + pattern_length;
3203 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3204 if (new_pos >= 0) {
3205 // A match.
3206 if (new_pos > match_end) {
3207 ReplacementStringBuilder::AddSubjectSlice(builder,
3208 match_end,
3209 new_pos);
3210 }
3211 pos = new_pos;
3212 builder->Add(pattern);
3213 } else {
3214 break;
3215 }
3216 }
3217 break;
3218 case SEARCH_LONG:
3219 while (pos <= max_search_start) {
3220 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003221 *match_pos = pos;
3222 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003223 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003224 int match_end = pos + pattern_length;
3225 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003226 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003227 // A match has been found.
3228 if (new_pos > match_end) {
3229 ReplacementStringBuilder::AddSubjectSlice(builder,
3230 match_end,
3231 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003232 }
3233 pos = new_pos;
3234 builder->Add(pattern);
3235 } else {
3236 break;
3237 }
3238 }
3239 break;
3240 }
3241 if (pos < max_search_start) {
3242 ReplacementStringBuilder::AddSubjectSlice(builder,
3243 pos + pattern_length,
3244 subject_length);
3245 }
3246 *match_pos = pos;
3247 return true;
3248}
3249
3250
3251static bool SearchStringMultiple(Handle<String> subject,
3252 Handle<String> pattern,
3253 Handle<JSArray> last_match_info,
3254 FixedArrayBuilder* builder) {
3255 ASSERT(subject->IsFlat());
3256 ASSERT(pattern->IsFlat());
3257 ASSERT(pattern->length() > 1);
3258
3259 // Treating as if a previous match was before first character.
3260 int match_pos = -pattern->length();
3261
3262 for (;;) { // Break when search complete.
3263 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3264 AssertNoAllocation no_gc;
3265 if (subject->IsAsciiRepresentation()) {
3266 Vector<const char> subject_vector = subject->ToAsciiVector();
3267 if (pattern->IsAsciiRepresentation()) {
3268 if (SearchStringMultiple(subject_vector,
3269 *pattern,
3270 pattern->ToAsciiVector(),
3271 builder,
3272 &match_pos)) break;
3273 } else {
3274 if (SearchStringMultiple(subject_vector,
3275 *pattern,
3276 pattern->ToUC16Vector(),
3277 builder,
3278 &match_pos)) break;
3279 }
3280 } else {
3281 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3282 if (pattern->IsAsciiRepresentation()) {
3283 if (SearchStringMultiple(subject_vector,
3284 *pattern,
3285 pattern->ToAsciiVector(),
3286 builder,
3287 &match_pos)) break;
3288 } else {
3289 if (SearchStringMultiple(subject_vector,
3290 *pattern,
3291 pattern->ToUC16Vector(),
3292 builder,
3293 &match_pos)) break;
3294 }
3295 }
3296 }
3297
3298 if (match_pos >= 0) {
3299 SetLastMatchInfoNoCaptures(subject,
3300 last_match_info,
3301 match_pos,
3302 match_pos + pattern->length());
3303 return true;
3304 }
3305 return false; // No matches at all.
3306}
3307
3308
3309static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3310 Handle<String> subject,
3311 Handle<JSRegExp> regexp,
3312 Handle<JSArray> last_match_array,
3313 FixedArrayBuilder* builder) {
3314 ASSERT(subject->IsFlat());
3315 int match_start = -1;
3316 int match_end = 0;
3317 int pos = 0;
3318 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3319 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3320
3321 OffsetsVector registers(required_registers);
3322 Vector<int> register_vector(registers.vector(), registers.length());
3323 int subject_length = subject->length();
3324
3325 for (;;) { // Break on failure, return on exception.
3326 RegExpImpl::IrregexpResult result =
3327 RegExpImpl::IrregexpExecOnce(regexp,
3328 subject,
3329 pos,
3330 register_vector);
3331 if (result == RegExpImpl::RE_SUCCESS) {
3332 match_start = register_vector[0];
3333 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3334 if (match_end < match_start) {
3335 ReplacementStringBuilder::AddSubjectSlice(builder,
3336 match_end,
3337 match_start);
3338 }
3339 match_end = register_vector[1];
3340 HandleScope loop_scope;
3341 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3342 if (match_start != match_end) {
3343 pos = match_end;
3344 } else {
3345 pos = match_end + 1;
3346 if (pos > subject_length) break;
3347 }
3348 } else if (result == RegExpImpl::RE_FAILURE) {
3349 break;
3350 } else {
3351 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3352 return result;
3353 }
3354 }
3355
3356 if (match_start >= 0) {
3357 if (match_end < subject_length) {
3358 ReplacementStringBuilder::AddSubjectSlice(builder,
3359 match_end,
3360 subject_length);
3361 }
3362 SetLastMatchInfoNoCaptures(subject,
3363 last_match_array,
3364 match_start,
3365 match_end);
3366 return RegExpImpl::RE_SUCCESS;
3367 } else {
3368 return RegExpImpl::RE_FAILURE; // No matches at all.
3369 }
3370}
3371
3372
3373static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3374 Handle<String> subject,
3375 Handle<JSRegExp> regexp,
3376 Handle<JSArray> last_match_array,
3377 FixedArrayBuilder* builder) {
3378
3379 ASSERT(subject->IsFlat());
3380 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3381 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3382
3383 OffsetsVector registers(required_registers);
3384 Vector<int> register_vector(registers.vector(), registers.length());
3385
3386 RegExpImpl::IrregexpResult result =
3387 RegExpImpl::IrregexpExecOnce(regexp,
3388 subject,
3389 0,
3390 register_vector);
3391
3392 int capture_count = regexp->CaptureCount();
3393 int subject_length = subject->length();
3394
3395 // Position to search from.
3396 int pos = 0;
3397 // End of previous match. Differs from pos if match was empty.
3398 int match_end = 0;
3399 if (result == RegExpImpl::RE_SUCCESS) {
3400 // Need to keep a copy of the previous match for creating last_match_info
3401 // at the end, so we have two vectors that we swap between.
3402 OffsetsVector registers2(required_registers);
3403 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3404
3405 do {
3406 int match_start = register_vector[0];
3407 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3408 if (match_end < match_start) {
3409 ReplacementStringBuilder::AddSubjectSlice(builder,
3410 match_end,
3411 match_start);
3412 }
3413 match_end = register_vector[1];
3414
3415 {
3416 // Avoid accumulating new handles inside loop.
3417 HandleScope temp_scope;
3418 // Arguments array to replace function is match, captures, index and
3419 // subject, i.e., 3 + capture count in total.
3420 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3421 elements->set(0, *Factory::NewSubString(subject,
3422 match_start,
3423 match_end));
3424 for (int i = 1; i <= capture_count; i++) {
3425 int start = register_vector[i * 2];
3426 if (start >= 0) {
3427 int end = register_vector[i * 2 + 1];
3428 ASSERT(start <= end);
3429 Handle<String> substring = Factory::NewSubString(subject,
3430 start,
3431 end);
3432 elements->set(i, *substring);
3433 } else {
3434 ASSERT(register_vector[i * 2 + 1] < 0);
3435 elements->set(i, Heap::undefined_value());
3436 }
3437 }
3438 elements->set(capture_count + 1, Smi::FromInt(match_start));
3439 elements->set(capture_count + 2, *subject);
3440 builder->Add(*Factory::NewJSArrayWithElements(elements));
3441 }
3442 // Swap register vectors, so the last successful match is in
3443 // prev_register_vector.
3444 Vector<int> tmp = prev_register_vector;
3445 prev_register_vector = register_vector;
3446 register_vector = tmp;
3447
3448 if (match_end > match_start) {
3449 pos = match_end;
3450 } else {
3451 pos = match_end + 1;
3452 if (pos > subject_length) {
3453 break;
3454 }
3455 }
3456
3457 result = RegExpImpl::IrregexpExecOnce(regexp,
3458 subject,
3459 pos,
3460 register_vector);
3461 } while (result == RegExpImpl::RE_SUCCESS);
3462
3463 if (result != RegExpImpl::RE_EXCEPTION) {
3464 // Finished matching, with at least one match.
3465 if (match_end < subject_length) {
3466 ReplacementStringBuilder::AddSubjectSlice(builder,
3467 match_end,
3468 subject_length);
3469 }
3470
3471 int last_match_capture_count = (capture_count + 1) * 2;
3472 int last_match_array_size =
3473 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3474 last_match_array->EnsureSize(last_match_array_size);
3475 AssertNoAllocation no_gc;
3476 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3477 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3478 RegExpImpl::SetLastSubject(elements, *subject);
3479 RegExpImpl::SetLastInput(elements, *subject);
3480 for (int i = 0; i < last_match_capture_count; i++) {
3481 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3482 }
3483 return RegExpImpl::RE_SUCCESS;
3484 }
3485 }
3486 // No matches at all, return failure or exception result directly.
3487 return result;
3488}
3489
3490
3491static Object* Runtime_RegExpExecMultiple(Arguments args) {
3492 ASSERT(args.length() == 4);
3493 HandleScope handles;
3494
3495 CONVERT_ARG_CHECKED(String, subject, 1);
3496 if (!subject->IsFlat()) { FlattenString(subject); }
3497 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3498 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3499 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3500
3501 ASSERT(last_match_info->HasFastElements());
3502 ASSERT(regexp->GetFlags().is_global());
3503 Handle<FixedArray> result_elements;
3504 if (result_array->HasFastElements()) {
3505 result_elements =
3506 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3507 } else {
3508 result_elements = Factory::NewFixedArrayWithHoles(16);
3509 }
3510 FixedArrayBuilder builder(result_elements);
3511
3512 if (regexp->TypeTag() == JSRegExp::ATOM) {
3513 Handle<String> pattern(
3514 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3515 int pattern_length = pattern->length();
3516 if (pattern_length == 1) {
3517 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3518 return *builder.ToJSArray(result_array);
3519 }
3520 return Heap::null_value();
3521 }
3522
3523 if (!pattern->IsFlat()) FlattenString(pattern);
3524 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3525 return *builder.ToJSArray(result_array);
3526 }
3527 return Heap::null_value();
3528 }
3529
3530 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3531
3532 RegExpImpl::IrregexpResult result;
3533 if (regexp->CaptureCount() == 0) {
3534 result = SearchRegExpNoCaptureMultiple(subject,
3535 regexp,
3536 last_match_info,
3537 &builder);
3538 } else {
3539 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3540 }
3541 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3542 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3543 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3544 return Failure::Exception();
3545}
3546
3547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003548static Object* Runtime_NumberToRadixString(Arguments args) {
3549 NoHandleAllocation ha;
3550 ASSERT(args.length() == 2);
3551
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003552 // Fast case where the result is a one character string.
3553 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3554 int value = Smi::cast(args[0])->value();
3555 int radix = Smi::cast(args[1])->value();
3556 if (value >= 0 && value < radix) {
3557 RUNTIME_ASSERT(radix <= 36);
3558 // Character array used for conversion.
3559 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3560 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3561 }
3562 }
3563
3564 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003565 CONVERT_DOUBLE_CHECKED(value, args[0]);
3566 if (isnan(value)) {
3567 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3568 }
3569 if (isinf(value)) {
3570 if (value < 0) {
3571 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3572 }
3573 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3574 }
3575 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3576 int radix = FastD2I(radix_number);
3577 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3578 char* str = DoubleToRadixCString(value, radix);
3579 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3580 DeleteArray(str);
3581 return result;
3582}
3583
3584
3585static Object* Runtime_NumberToFixed(Arguments args) {
3586 NoHandleAllocation ha;
3587 ASSERT(args.length() == 2);
3588
3589 CONVERT_DOUBLE_CHECKED(value, args[0]);
3590 if (isnan(value)) {
3591 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3592 }
3593 if (isinf(value)) {
3594 if (value < 0) {
3595 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3596 }
3597 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3598 }
3599 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3600 int f = FastD2I(f_number);
3601 RUNTIME_ASSERT(f >= 0);
3602 char* str = DoubleToFixedCString(value, f);
3603 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3604 DeleteArray(str);
3605 return res;
3606}
3607
3608
3609static Object* Runtime_NumberToExponential(Arguments args) {
3610 NoHandleAllocation ha;
3611 ASSERT(args.length() == 2);
3612
3613 CONVERT_DOUBLE_CHECKED(value, args[0]);
3614 if (isnan(value)) {
3615 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3616 }
3617 if (isinf(value)) {
3618 if (value < 0) {
3619 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3620 }
3621 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3622 }
3623 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3624 int f = FastD2I(f_number);
3625 RUNTIME_ASSERT(f >= -1 && f <= 20);
3626 char* str = DoubleToExponentialCString(value, f);
3627 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3628 DeleteArray(str);
3629 return res;
3630}
3631
3632
3633static Object* Runtime_NumberToPrecision(Arguments args) {
3634 NoHandleAllocation ha;
3635 ASSERT(args.length() == 2);
3636
3637 CONVERT_DOUBLE_CHECKED(value, args[0]);
3638 if (isnan(value)) {
3639 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3640 }
3641 if (isinf(value)) {
3642 if (value < 0) {
3643 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3644 }
3645 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3646 }
3647 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3648 int f = FastD2I(f_number);
3649 RUNTIME_ASSERT(f >= 1 && f <= 21);
3650 char* str = DoubleToPrecisionCString(value, f);
3651 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3652 DeleteArray(str);
3653 return res;
3654}
3655
3656
3657// Returns a single character string where first character equals
3658// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003659static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003660 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003661 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003662 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003663 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003664 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003665 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003666}
3667
3668
3669Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3670 // Handle [] indexing on Strings
3671 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003672 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3673 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003674 }
3675
3676 // Handle [] indexing on String objects
3677 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003678 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3679 Handle<Object> result =
3680 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3681 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003682 }
3683
3684 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003685 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686 return prototype->GetElement(index);
3687 }
3688
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003689 return GetElement(object, index);
3690}
3691
3692
3693Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003694 return object->GetElement(index);
3695}
3696
3697
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003698Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3699 HandleScope scope;
3700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003701 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003702 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003703 Handle<Object> error =
3704 Factory::NewTypeError("non_object_property_load",
3705 HandleVector(args, 2));
3706 return Top::Throw(*error);
3707 }
3708
3709 // Check if the given key is an array index.
3710 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003711 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003712 return GetElementOrCharAt(object, index);
3713 }
3714
3715 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003716 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003717 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003718 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003720 bool has_pending_exception = false;
3721 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003722 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003724 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725 }
3726
ager@chromium.org32912102009-01-16 10:38:43 +00003727 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728 // the element if so.
3729 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003730 return GetElementOrCharAt(object, index);
3731 } else {
3732 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003733 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003734 }
3735}
3736
3737
3738static Object* Runtime_GetProperty(Arguments args) {
3739 NoHandleAllocation ha;
3740 ASSERT(args.length() == 2);
3741
3742 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003743 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003744
3745 return Runtime::GetObjectProperty(object, key);
3746}
3747
3748
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003749// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003750static Object* Runtime_KeyedGetProperty(Arguments args) {
3751 NoHandleAllocation ha;
3752 ASSERT(args.length() == 2);
3753
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003754 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003755 // itself.
3756 //
3757 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003758 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003759 // global proxy object never has properties. This is the case
3760 // because the global proxy object forwards everything to its hidden
3761 // prototype including local lookups.
3762 //
3763 // Additionally, we need to make sure that we do not cache results
3764 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003765 if (args[0]->IsJSObject() &&
3766 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003767 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003768 args[1]->IsString()) {
3769 JSObject* receiver = JSObject::cast(args[0]);
3770 String* key = String::cast(args[1]);
3771 if (receiver->HasFastProperties()) {
3772 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003773 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003774 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3775 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003776 Object* value = receiver->FastPropertyAt(offset);
3777 return value->IsTheHole() ? Heap::undefined_value() : value;
3778 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003779 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003780 LookupResult result;
3781 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003782 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003783 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003784 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003785 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003786 }
3787 } else {
3788 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003789 StringDictionary* dictionary = receiver->property_dictionary();
3790 int entry = dictionary->FindEntry(key);
3791 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003792 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003793 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003794 if (!receiver->IsGlobalObject()) return value;
3795 value = JSGlobalPropertyCell::cast(value)->value();
3796 if (!value->IsTheHole()) return value;
3797 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003798 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003799 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003800 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3801 // Fast case for string indexing using [] with a smi index.
3802 HandleScope scope;
3803 Handle<String> str = args.at<String>(0);
3804 int index = Smi::cast(args[1])->value();
3805 Handle<Object> result = GetCharAt(str, index);
3806 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003807 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003808
3809 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003810 return Runtime::GetObjectProperty(args.at<Object>(0),
3811 args.at<Object>(1));
3812}
3813
3814
ager@chromium.org5c838252010-02-19 08:53:10 +00003815static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3816 ASSERT(args.length() == 5);
3817 HandleScope scope;
3818 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3819 CONVERT_CHECKED(String, name, args[1]);
3820 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3821 CONVERT_CHECKED(JSFunction, fun, args[3]);
3822 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3823 int unchecked = flag_attr->value();
3824 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3825 RUNTIME_ASSERT(!obj->IsNull());
3826 LookupResult result;
3827 obj->LocalLookupRealNamedProperty(name, &result);
3828
3829 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3830 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3831 // delete it to avoid running into trouble in DefineAccessor, which
3832 // handles this incorrectly if the property is readonly (does nothing)
3833 if (result.IsProperty() &&
3834 (result.type() == FIELD || result.type() == NORMAL
3835 || result.type() == CONSTANT_FUNCTION)) {
3836 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3837 }
3838 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3839}
3840
3841static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3842 ASSERT(args.length() == 4);
3843 HandleScope scope;
3844 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3845 CONVERT_ARG_CHECKED(String, name, 1);
3846 Handle<Object> obj_value = args.at<Object>(2);
3847
3848 CONVERT_CHECKED(Smi, flag, args[3]);
3849 int unchecked = flag->value();
3850 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3851
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003852 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3853
3854 // Check if this is an element.
3855 uint32_t index;
3856 bool is_element = name->AsArrayIndex(&index);
3857
3858 // Special case for elements if any of the flags are true.
3859 // If elements are in fast case we always implicitly assume that:
3860 // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
3861 if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
3862 is_element) {
3863 // Normalize the elements to enable attributes on the property.
3864 js_object->NormalizeElements();
3865 NumberDictionary* dictionary = js_object->element_dictionary();
3866 // Make sure that we never go back to fast case.
3867 dictionary->set_requires_slow_elements();
3868 PropertyDetails details = PropertyDetails(attr, NORMAL);
3869 dictionary->Set(index, *obj_value, details);
3870 }
3871
ager@chromium.org5c838252010-02-19 08:53:10 +00003872 LookupResult result;
3873 js_object->LocalLookupRealNamedProperty(*name, &result);
3874
ager@chromium.org5c838252010-02-19 08:53:10 +00003875 // Take special care when attributes are different and there is already
3876 // a property. For simplicity we normalize the property which enables us
3877 // to not worry about changing the instance_descriptor and creating a new
3878 // map. The current version of SetObjectProperty does not handle attributes
3879 // correctly in the case where a property is a field and is reset with
3880 // new attributes.
3881 if (result.IsProperty() && attr != result.GetAttributes()) {
3882 // New attributes - normalize to avoid writing to instance descriptor
3883 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3884 // Use IgnoreAttributes version since a readonly property may be
3885 // overridden and SetProperty does not allow this.
3886 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3887 *obj_value,
3888 attr);
3889 }
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00003890
ager@chromium.org5c838252010-02-19 08:53:10 +00003891 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3892}
3893
3894
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895Object* Runtime::SetObjectProperty(Handle<Object> object,
3896 Handle<Object> key,
3897 Handle<Object> value,
3898 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003899 HandleScope scope;
3900
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003901 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003902 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003903 Handle<Object> error =
3904 Factory::NewTypeError("non_object_property_store",
3905 HandleVector(args, 2));
3906 return Top::Throw(*error);
3907 }
3908
3909 // If the object isn't a JavaScript object, we ignore the store.
3910 if (!object->IsJSObject()) return *value;
3911
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003912 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003914 // Check if the given key is an array index.
3915 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003916 if (key->ToArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3918 // of a string using [] notation. We need to support this too in
3919 // JavaScript.
3920 // In the case of a String object we just need to redirect the assignment to
3921 // the underlying string if the index is in range. Since the underlying
3922 // string does nothing with the assignment then we can ignore such
3923 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003924 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003925 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003926 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003928 Handle<Object> result = SetElement(js_object, index, value);
3929 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003930 return *value;
3931 }
3932
3933 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003934 Handle<Object> result;
3935 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003936 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003937 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003938 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003939 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003940 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003941 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003942 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003943 return *value;
3944 }
3945
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003947 bool has_pending_exception = false;
3948 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3949 if (has_pending_exception) return Failure::Exception();
3950 Handle<String> name = Handle<String>::cast(converted);
3951
3952 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003953 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003954 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003955 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003956 }
3957}
3958
3959
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003960Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3961 Handle<Object> key,
3962 Handle<Object> value,
3963 PropertyAttributes attr) {
3964 HandleScope scope;
3965
3966 // Check if the given key is an array index.
3967 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003968 if (key->ToArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003969 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3970 // of a string using [] notation. We need to support this too in
3971 // JavaScript.
3972 // In the case of a String object we just need to redirect the assignment to
3973 // the underlying string if the index is in range. Since the underlying
3974 // string does nothing with the assignment then we can ignore such
3975 // assignments.
3976 if (js_object->IsStringObjectWithCharacterAt(index)) {
3977 return *value;
3978 }
3979
3980 return js_object->SetElement(index, *value);
3981 }
3982
3983 if (key->IsString()) {
3984 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003985 return js_object->SetElement(index, *value);
3986 } else {
3987 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003988 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003989 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3990 *value,
3991 attr);
3992 }
3993 }
3994
3995 // Call-back into JavaScript to convert the key to a string.
3996 bool has_pending_exception = false;
3997 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3998 if (has_pending_exception) return Failure::Exception();
3999 Handle<String> name = Handle<String>::cast(converted);
4000
4001 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004002 return js_object->SetElement(index, *value);
4003 } else {
4004 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
4005 }
4006}
4007
4008
ager@chromium.orge2902be2009-06-08 12:21:35 +00004009Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
4010 Handle<Object> key) {
4011 HandleScope scope;
4012
4013 // Check if the given key is an array index.
4014 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004015 if (key->ToArrayIndex(&index)) {
ager@chromium.orge2902be2009-06-08 12:21:35 +00004016 // In Firefox/SpiderMonkey, Safari and Opera you can access the
4017 // characters of a string using [] notation. In the case of a
4018 // String object we just need to redirect the deletion to the
4019 // underlying string if the index is in range. Since the
4020 // underlying string does nothing with the deletion, we can ignore
4021 // such deletions.
4022 if (js_object->IsStringObjectWithCharacterAt(index)) {
4023 return Heap::true_value();
4024 }
4025
4026 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
4027 }
4028
4029 Handle<String> key_string;
4030 if (key->IsString()) {
4031 key_string = Handle<String>::cast(key);
4032 } else {
4033 // Call-back into JavaScript to convert the key to a string.
4034 bool has_pending_exception = false;
4035 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
4036 if (has_pending_exception) return Failure::Exception();
4037 key_string = Handle<String>::cast(converted);
4038 }
4039
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004040 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00004041 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
4042}
4043
4044
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004045static Object* Runtime_SetProperty(Arguments args) {
4046 NoHandleAllocation ha;
4047 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4048
4049 Handle<Object> object = args.at<Object>(0);
4050 Handle<Object> key = args.at<Object>(1);
4051 Handle<Object> value = args.at<Object>(2);
4052
4053 // Compute attributes.
4054 PropertyAttributes attributes = NONE;
4055 if (args.length() == 4) {
4056 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004057 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004058 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004059 RUNTIME_ASSERT(
4060 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4061 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004062 }
4063 return Runtime::SetObjectProperty(object, key, value, attributes);
4064}
4065
4066
4067// Set a local property, even if it is READ_ONLY. If the property does not
4068// exist, it will be added with attributes NONE.
4069static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4070 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004071 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004072 CONVERT_CHECKED(JSObject, object, args[0]);
4073 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004074 // Compute attributes.
4075 PropertyAttributes attributes = NONE;
4076 if (args.length() == 4) {
4077 CONVERT_CHECKED(Smi, value_obj, args[3]);
4078 int unchecked_value = value_obj->value();
4079 // Only attribute bits should be set.
4080 RUNTIME_ASSERT(
4081 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4082 attributes = static_cast<PropertyAttributes>(unchecked_value);
4083 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004084
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004085 return object->
4086 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004087}
4088
4089
4090static Object* Runtime_DeleteProperty(Arguments args) {
4091 NoHandleAllocation ha;
4092 ASSERT(args.length() == 2);
4093
4094 CONVERT_CHECKED(JSObject, object, args[0]);
4095 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004096 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004097}
4098
4099
ager@chromium.org9085a012009-05-11 19:22:57 +00004100static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4101 Handle<String> key) {
4102 if (object->HasLocalProperty(*key)) return Heap::true_value();
4103 // Handle hidden prototypes. If there's a hidden prototype above this thing
4104 // then we have to check it for properties, because they are supposed to
4105 // look like they are on this object.
4106 Handle<Object> proto(object->GetPrototype());
4107 if (proto->IsJSObject() &&
4108 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4109 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4110 }
4111 return Heap::false_value();
4112}
4113
4114
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004115static Object* Runtime_HasLocalProperty(Arguments args) {
4116 NoHandleAllocation ha;
4117 ASSERT(args.length() == 2);
4118 CONVERT_CHECKED(String, key, args[1]);
4119
ager@chromium.org9085a012009-05-11 19:22:57 +00004120 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004121 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004122 if (obj->IsJSObject()) {
4123 JSObject* object = JSObject::cast(obj);
4124 // Fast case - no interceptors.
4125 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4126 // Slow case. Either it's not there or we have an interceptor. We should
4127 // have handles for this kind of deal.
4128 HandleScope scope;
4129 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4130 Handle<String>(key));
4131 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004132 // Well, there is one exception: Handle [] on strings.
4133 uint32_t index;
4134 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004135 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004136 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004137 return Heap::true_value();
4138 }
4139 }
4140 return Heap::false_value();
4141}
4142
4143
4144static Object* Runtime_HasProperty(Arguments args) {
4145 NoHandleAllocation na;
4146 ASSERT(args.length() == 2);
4147
4148 // Only JS objects can have properties.
4149 if (args[0]->IsJSObject()) {
4150 JSObject* object = JSObject::cast(args[0]);
4151 CONVERT_CHECKED(String, key, args[1]);
4152 if (object->HasProperty(key)) return Heap::true_value();
4153 }
4154 return Heap::false_value();
4155}
4156
4157
4158static Object* Runtime_HasElement(Arguments args) {
4159 NoHandleAllocation na;
4160 ASSERT(args.length() == 2);
4161
4162 // Only JS objects can have elements.
4163 if (args[0]->IsJSObject()) {
4164 JSObject* object = JSObject::cast(args[0]);
4165 CONVERT_CHECKED(Smi, index_obj, args[1]);
4166 uint32_t index = index_obj->value();
4167 if (object->HasElement(index)) return Heap::true_value();
4168 }
4169 return Heap::false_value();
4170}
4171
4172
4173static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4174 NoHandleAllocation ha;
4175 ASSERT(args.length() == 2);
4176
4177 CONVERT_CHECKED(JSObject, object, args[0]);
4178 CONVERT_CHECKED(String, key, args[1]);
4179
4180 uint32_t index;
4181 if (key->AsArrayIndex(&index)) {
4182 return Heap::ToBoolean(object->HasElement(index));
4183 }
4184
ager@chromium.org870a0b62008-11-04 11:43:05 +00004185 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4186 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004187}
4188
4189
4190static Object* Runtime_GetPropertyNames(Arguments args) {
4191 HandleScope scope;
4192 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004193 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004194 return *GetKeysFor(object);
4195}
4196
4197
4198// Returns either a FixedArray as Runtime_GetPropertyNames,
4199// or, if the given object has an enum cache that contains
4200// all enumerable properties of the object and its prototypes
4201// have none, the map of the object. This is used to speed up
4202// the check for deletions during a for-in.
4203static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4204 ASSERT(args.length() == 1);
4205
4206 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4207
4208 if (raw_object->IsSimpleEnum()) return raw_object->map();
4209
4210 HandleScope scope;
4211 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004212 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4213 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004214
4215 // Test again, since cache may have been built by preceding call.
4216 if (object->IsSimpleEnum()) return object->map();
4217
4218 return *content;
4219}
4220
4221
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004222// Find the length of the prototype chain that is to to handled as one. If a
4223// prototype object is hidden it is to be viewed as part of the the object it
4224// is prototype for.
4225static int LocalPrototypeChainLength(JSObject* obj) {
4226 int count = 1;
4227 Object* proto = obj->GetPrototype();
4228 while (proto->IsJSObject() &&
4229 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4230 count++;
4231 proto = JSObject::cast(proto)->GetPrototype();
4232 }
4233 return count;
4234}
4235
4236
4237// Return the names of the local named properties.
4238// args[0]: object
4239static Object* Runtime_GetLocalPropertyNames(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 // Skip the global proxy as it has no properties and always delegates to the
4248 // real global object.
4249 if (obj->IsJSGlobalProxy()) {
4250 // Only collect names if access is permitted.
4251 if (obj->IsAccessCheckNeeded() &&
4252 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4253 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4254 return *Factory::NewJSArray(0);
4255 }
4256 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4257 }
4258
4259 // Find the number of objects making up this.
4260 int length = LocalPrototypeChainLength(*obj);
4261
4262 // Find the number of local properties for each of the objects.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004263 ScopedVector<int> local_property_count(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004264 int total_property_count = 0;
4265 Handle<JSObject> jsproto = obj;
4266 for (int i = 0; i < length; i++) {
4267 // Only collect names if access is permitted.
4268 if (jsproto->IsAccessCheckNeeded() &&
4269 !Top::MayNamedAccess(*jsproto,
4270 Heap::undefined_value(),
4271 v8::ACCESS_KEYS)) {
4272 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4273 return *Factory::NewJSArray(0);
4274 }
4275 int n;
4276 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4277 local_property_count[i] = n;
4278 total_property_count += n;
4279 if (i < length - 1) {
4280 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4281 }
4282 }
4283
4284 // Allocate an array with storage for all the property names.
4285 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4286
4287 // Get the property names.
4288 jsproto = obj;
4289 int proto_with_hidden_properties = 0;
4290 for (int i = 0; i < length; i++) {
4291 jsproto->GetLocalPropertyNames(*names,
4292 i == 0 ? 0 : local_property_count[i - 1]);
4293 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4294 proto_with_hidden_properties++;
4295 }
4296 if (i < length - 1) {
4297 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4298 }
4299 }
4300
4301 // Filter out name of hidden propeties object.
4302 if (proto_with_hidden_properties > 0) {
4303 Handle<FixedArray> old_names = names;
4304 names = Factory::NewFixedArray(
4305 names->length() - proto_with_hidden_properties);
4306 int dest_pos = 0;
4307 for (int i = 0; i < total_property_count; i++) {
4308 Object* name = old_names->get(i);
4309 if (name == Heap::hidden_symbol()) {
4310 continue;
4311 }
4312 names->set(dest_pos++, name);
4313 }
4314 }
4315
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004316 return *Factory::NewJSArrayWithElements(names);
4317}
4318
4319
4320// Return the names of the local indexed properties.
4321// args[0]: object
4322static Object* Runtime_GetLocalElementNames(Arguments args) {
4323 HandleScope scope;
4324 ASSERT(args.length() == 1);
4325 if (!args[0]->IsJSObject()) {
4326 return Heap::undefined_value();
4327 }
4328 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4329
4330 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4331 Handle<FixedArray> names = Factory::NewFixedArray(n);
4332 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4333 return *Factory::NewJSArrayWithElements(names);
4334}
4335
4336
4337// Return information on whether an object has a named or indexed interceptor.
4338// args[0]: object
4339static Object* Runtime_GetInterceptorInfo(Arguments args) {
4340 HandleScope scope;
4341 ASSERT(args.length() == 1);
4342 if (!args[0]->IsJSObject()) {
4343 return Smi::FromInt(0);
4344 }
4345 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4346
4347 int result = 0;
4348 if (obj->HasNamedInterceptor()) result |= 2;
4349 if (obj->HasIndexedInterceptor()) result |= 1;
4350
4351 return Smi::FromInt(result);
4352}
4353
4354
4355// Return property names from named interceptor.
4356// args[0]: object
4357static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4358 HandleScope scope;
4359 ASSERT(args.length() == 1);
4360 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4361
4362 if (obj->HasNamedInterceptor()) {
4363 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4364 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4365 }
4366 return Heap::undefined_value();
4367}
4368
4369
4370// Return element names from indexed interceptor.
4371// args[0]: object
4372static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4373 HandleScope scope;
4374 ASSERT(args.length() == 1);
4375 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4376
4377 if (obj->HasIndexedInterceptor()) {
4378 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4379 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4380 }
4381 return Heap::undefined_value();
4382}
4383
4384
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004385static Object* Runtime_LocalKeys(Arguments args) {
4386 ASSERT_EQ(args.length(), 1);
4387 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4388 HandleScope scope;
4389 Handle<JSObject> object(raw_object);
4390 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4391 LOCAL_ONLY);
4392 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4393 // property array and since the result is mutable we have to create
4394 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004395 int length = contents->length();
4396 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4397 for (int i = 0; i < length; i++) {
4398 Object* entry = contents->get(i);
4399 if (entry->IsString()) {
4400 copy->set(i, entry);
4401 } else {
4402 ASSERT(entry->IsNumber());
4403 HandleScope scope;
4404 Handle<Object> entry_handle(entry);
4405 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4406 copy->set(i, *entry_str);
4407 }
4408 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004409 return *Factory::NewJSArrayWithElements(copy);
4410}
4411
4412
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004413static Object* Runtime_GetArgumentsProperty(Arguments args) {
4414 NoHandleAllocation ha;
4415 ASSERT(args.length() == 1);
4416
4417 // Compute the frame holding the arguments.
4418 JavaScriptFrameIterator it;
4419 it.AdvanceToArgumentsFrame();
4420 JavaScriptFrame* frame = it.frame();
4421
4422 // Get the actual number of provided arguments.
4423 const uint32_t n = frame->GetProvidedParametersCount();
4424
4425 // Try to convert the key to an index. If successful and within
4426 // index return the the argument from the frame.
4427 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004428 if (args[0]->ToArrayIndex(&index) && index < n) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004429 return frame->GetParameter(index);
4430 }
4431
4432 // Convert the key to a string.
4433 HandleScope scope;
4434 bool exception = false;
4435 Handle<Object> converted =
4436 Execution::ToString(args.at<Object>(0), &exception);
4437 if (exception) return Failure::Exception();
4438 Handle<String> key = Handle<String>::cast(converted);
4439
4440 // Try to convert the string key into an array index.
4441 if (key->AsArrayIndex(&index)) {
4442 if (index < n) {
4443 return frame->GetParameter(index);
4444 } else {
4445 return Top::initial_object_prototype()->GetElement(index);
4446 }
4447 }
4448
4449 // Handle special arguments properties.
4450 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4451 if (key->Equals(Heap::callee_symbol())) return frame->function();
4452
4453 // Lookup in the initial Object.prototype object.
4454 return Top::initial_object_prototype()->GetProperty(*key);
4455}
4456
4457
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004458static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004459 HandleScope scope;
4460
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004461 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004462 Handle<Object> object = args.at<Object>(0);
4463 if (object->IsJSObject()) {
4464 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004465 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4466 js_object->TransformToFastProperties(0);
4467 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004468 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004469 return *object;
4470}
4471
4472
4473static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004474 HandleScope scope;
4475
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004476 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004477 Handle<Object> object = args.at<Object>(0);
4478 if (object->IsJSObject()) {
4479 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004480 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004481 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004482 return *object;
4483}
4484
4485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004486static Object* Runtime_ToBool(Arguments args) {
4487 NoHandleAllocation ha;
4488 ASSERT(args.length() == 1);
4489
4490 return args[0]->ToBoolean();
4491}
4492
4493
4494// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4495// Possible optimizations: put the type string into the oddballs.
4496static Object* Runtime_Typeof(Arguments args) {
4497 NoHandleAllocation ha;
4498
4499 Object* obj = args[0];
4500 if (obj->IsNumber()) return Heap::number_symbol();
4501 HeapObject* heap_obj = HeapObject::cast(obj);
4502
4503 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004504 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004505
4506 InstanceType instance_type = heap_obj->map()->instance_type();
4507 if (instance_type < FIRST_NONSTRING_TYPE) {
4508 return Heap::string_symbol();
4509 }
4510
4511 switch (instance_type) {
4512 case ODDBALL_TYPE:
4513 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4514 return Heap::boolean_symbol();
4515 }
4516 if (heap_obj->IsNull()) {
4517 return Heap::object_symbol();
4518 }
4519 ASSERT(heap_obj->IsUndefined());
4520 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004521 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004522 return Heap::function_symbol();
4523 default:
4524 // For any kind of object not handled above, the spec rule for
4525 // host objects gives that it is okay to return "object"
4526 return Heap::object_symbol();
4527 }
4528}
4529
4530
lrn@chromium.org25156de2010-04-06 13:10:27 +00004531static bool AreDigits(const char*s, int from, int to) {
4532 for (int i = from; i < to; i++) {
4533 if (s[i] < '0' || s[i] > '9') return false;
4534 }
4535
4536 return true;
4537}
4538
4539
4540static int ParseDecimalInteger(const char*s, int from, int to) {
4541 ASSERT(to - from < 10); // Overflow is not possible.
4542 ASSERT(from < to);
4543 int d = s[from] - '0';
4544
4545 for (int i = from + 1; i < to; i++) {
4546 d = 10 * d + (s[i] - '0');
4547 }
4548
4549 return d;
4550}
4551
4552
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004553static Object* Runtime_StringToNumber(Arguments args) {
4554 NoHandleAllocation ha;
4555 ASSERT(args.length() == 1);
4556 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004557 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004558
4559 // Fast case: short integer or some sorts of junk values.
4560 int len = subject->length();
4561 if (subject->IsSeqAsciiString()) {
4562 if (len == 0) return Smi::FromInt(0);
4563
4564 char const* data = SeqAsciiString::cast(subject)->GetChars();
4565 bool minus = (data[0] == '-');
4566 int start_pos = (minus ? 1 : 0);
4567
4568 if (start_pos == len) {
4569 return Heap::nan_value();
4570 } else if (data[start_pos] > '9') {
4571 // Fast check for a junk value. A valid string may start from a
4572 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4573 // the 'I' character ('Infinity'). All of that have codes not greater than
4574 // '9' except 'I'.
4575 if (data[start_pos] != 'I') {
4576 return Heap::nan_value();
4577 }
4578 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4579 // The maximal/minimal smi has 10 digits. If the string has less digits we
4580 // know it will fit into the smi-data type.
4581 int d = ParseDecimalInteger(data, start_pos, len);
4582 if (minus) {
4583 if (d == 0) return Heap::minus_zero_value();
4584 d = -d;
4585 }
4586 return Smi::FromInt(d);
4587 }
4588 }
4589
4590 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4592}
4593
4594
4595static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4596 NoHandleAllocation ha;
4597 ASSERT(args.length() == 1);
4598
4599 CONVERT_CHECKED(JSArray, codes, args[0]);
4600 int length = Smi::cast(codes->length())->value();
4601
4602 // Check if the string can be ASCII.
4603 int i;
4604 for (i = 0; i < length; i++) {
4605 Object* element = codes->GetElement(i);
4606 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4607 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4608 break;
4609 }
4610
4611 Object* object = NULL;
4612 if (i == length) { // The string is ASCII.
4613 object = Heap::AllocateRawAsciiString(length);
4614 } else { // The string is not ASCII.
4615 object = Heap::AllocateRawTwoByteString(length);
4616 }
4617
4618 if (object->IsFailure()) return object;
4619 String* result = String::cast(object);
4620 for (int i = 0; i < length; i++) {
4621 Object* element = codes->GetElement(i);
4622 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004623 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004624 }
4625 return result;
4626}
4627
4628
4629// kNotEscaped is generated by the following:
4630//
4631// #!/bin/perl
4632// for (my $i = 0; $i < 256; $i++) {
4633// print "\n" if $i % 16 == 0;
4634// my $c = chr($i);
4635// my $escaped = 1;
4636// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4637// print $escaped ? "0, " : "1, ";
4638// }
4639
4640
4641static bool IsNotEscaped(uint16_t character) {
4642 // Only for 8 bit characters, the rest are always escaped (in a different way)
4643 ASSERT(character < 256);
4644 static const char kNotEscaped[256] = {
4645 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4646 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4647 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4648 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4649 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4650 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4651 0, 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, 0, 0, 0, 0, 0,
4653 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4654 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4655 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4656 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4661 };
4662 return kNotEscaped[character] != 0;
4663}
4664
4665
4666static Object* Runtime_URIEscape(Arguments args) {
4667 const char hex_chars[] = "0123456789ABCDEF";
4668 NoHandleAllocation ha;
4669 ASSERT(args.length() == 1);
4670 CONVERT_CHECKED(String, source, args[0]);
4671
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004672 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004673
4674 int escaped_length = 0;
4675 int length = source->length();
4676 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004677 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004678 buffer->Reset(source);
4679 while (buffer->has_more()) {
4680 uint16_t character = buffer->GetNext();
4681 if (character >= 256) {
4682 escaped_length += 6;
4683 } else if (IsNotEscaped(character)) {
4684 escaped_length++;
4685 } else {
4686 escaped_length += 3;
4687 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004688 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004689 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004690 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004691 Top::context()->mark_out_of_memory();
4692 return Failure::OutOfMemoryException();
4693 }
4694 }
4695 }
4696 // No length change implies no change. Return original string if no change.
4697 if (escaped_length == length) {
4698 return source;
4699 }
4700 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4701 if (o->IsFailure()) return o;
4702 String* destination = String::cast(o);
4703 int dest_position = 0;
4704
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004705 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004706 buffer->Rewind();
4707 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004708 uint16_t chr = buffer->GetNext();
4709 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004710 destination->Set(dest_position, '%');
4711 destination->Set(dest_position+1, 'u');
4712 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4713 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4714 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4715 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004716 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004717 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004718 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004719 dest_position++;
4720 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004721 destination->Set(dest_position, '%');
4722 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4723 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004724 dest_position += 3;
4725 }
4726 }
4727 return destination;
4728}
4729
4730
4731static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4732 static const signed char kHexValue['g'] = {
4733 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4734 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4735 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4736 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4737 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4738 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4739 -1, 10, 11, 12, 13, 14, 15 };
4740
4741 if (character1 > 'f') return -1;
4742 int hi = kHexValue[character1];
4743 if (hi == -1) return -1;
4744 if (character2 > 'f') return -1;
4745 int lo = kHexValue[character2];
4746 if (lo == -1) return -1;
4747 return (hi << 4) + lo;
4748}
4749
4750
ager@chromium.org870a0b62008-11-04 11:43:05 +00004751static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004752 int i,
4753 int length,
4754 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004755 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004756 int32_t hi = 0;
4757 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004758 if (character == '%' &&
4759 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004760 source->Get(i + 1) == 'u' &&
4761 (hi = TwoDigitHex(source->Get(i + 2),
4762 source->Get(i + 3))) != -1 &&
4763 (lo = TwoDigitHex(source->Get(i + 4),
4764 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 *step = 6;
4766 return (hi << 8) + lo;
4767 } else if (character == '%' &&
4768 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004769 (lo = TwoDigitHex(source->Get(i + 1),
4770 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004771 *step = 3;
4772 return lo;
4773 } else {
4774 *step = 1;
4775 return character;
4776 }
4777}
4778
4779
4780static Object* Runtime_URIUnescape(Arguments args) {
4781 NoHandleAllocation ha;
4782 ASSERT(args.length() == 1);
4783 CONVERT_CHECKED(String, source, args[0]);
4784
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004785 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004786
4787 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004788 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004789
4790 int unescaped_length = 0;
4791 for (int i = 0; i < length; unescaped_length++) {
4792 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004793 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004794 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004795 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004796 i += step;
4797 }
4798
4799 // No length change implies no change. Return original string if no change.
4800 if (unescaped_length == length)
4801 return source;
4802
4803 Object* o = ascii ?
4804 Heap::AllocateRawAsciiString(unescaped_length) :
4805 Heap::AllocateRawTwoByteString(unescaped_length);
4806 if (o->IsFailure()) return o;
4807 String* destination = String::cast(o);
4808
4809 int dest_position = 0;
4810 for (int i = 0; i < length; dest_position++) {
4811 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004812 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004813 i += step;
4814 }
4815 return destination;
4816}
4817
4818
4819static Object* Runtime_StringParseInt(Arguments args) {
4820 NoHandleAllocation ha;
4821
4822 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004823 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004824
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004825 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004826
lrn@chromium.org25156de2010-04-06 13:10:27 +00004827 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4828 double value = StringToInt(s, radix);
4829 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004830 return Heap::nan_value();
4831}
4832
4833
4834static Object* Runtime_StringParseFloat(Arguments args) {
4835 NoHandleAllocation ha;
4836 CONVERT_CHECKED(String, str, args[0]);
4837
4838 // ECMA-262 section 15.1.2.3, empty string is NaN
4839 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4840
4841 // Create a number object from the value.
4842 return Heap::NumberFromDouble(value);
4843}
4844
4845
4846static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4847static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4848
4849
4850template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004851static Object* ConvertCaseHelper(String* s,
4852 int length,
4853 int input_string_length,
4854 unibrow::Mapping<Converter, 128>* mapping) {
4855 // We try this twice, once with the assumption that the result is no longer
4856 // than the input and, if that assumption breaks, again with the exact
4857 // length. This may not be pretty, but it is nicer than what was here before
4858 // and I hereby claim my vaffel-is.
4859 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860 // Allocate the resulting string.
4861 //
4862 // NOTE: This assumes that the upper/lower case of an ascii
4863 // character is also ascii. This is currently the case, but it
4864 // might break in the future if we implement more context and locale
4865 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004866 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004867 ? Heap::AllocateRawAsciiString(length)
4868 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004869 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870 String* result = String::cast(o);
4871 bool has_changed_character = false;
4872
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004873 // Convert all characters to upper case, assuming that they will fit
4874 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004875 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004876 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004877 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878 // We can assume that the string is not empty
4879 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004880 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004881 bool has_next = buffer->has_more();
4882 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883 int char_length = mapping->get(current, next, chars);
4884 if (char_length == 0) {
4885 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004886 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004887 i++;
4888 } else if (char_length == 1) {
4889 // Common case: converting the letter resulted in one character.
4890 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004891 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892 has_changed_character = true;
4893 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004894 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895 // We've assumed that the result would be as long as the
4896 // input but here is a character that converts to several
4897 // characters. No matter, we calculate the exact length
4898 // of the result and try the whole thing again.
4899 //
4900 // Note that this leaves room for optimization. We could just
4901 // memcpy what we already have to the result string. Also,
4902 // the result string is the last object allocated we could
4903 // "realloc" it and probably, in the vast majority of cases,
4904 // extend the existing string to be able to hold the full
4905 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004906 int next_length = 0;
4907 if (has_next) {
4908 next_length = mapping->get(next, 0, chars);
4909 if (next_length == 0) next_length = 1;
4910 }
4911 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004912 while (buffer->has_more()) {
4913 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004914 // NOTE: we use 0 as the next character here because, while
4915 // the next character may affect what a character converts to,
4916 // it does not in any case affect the length of what it convert
4917 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004918 int char_length = mapping->get(current, 0, chars);
4919 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004920 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004921 if (current_length > Smi::kMaxValue) {
4922 Top::context()->mark_out_of_memory();
4923 return Failure::OutOfMemoryException();
4924 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004925 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004926 // Try again with the real length.
4927 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004928 } else {
4929 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004930 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004931 i++;
4932 }
4933 has_changed_character = true;
4934 }
4935 current = next;
4936 }
4937 if (has_changed_character) {
4938 return result;
4939 } else {
4940 // If we didn't actually change anything in doing the conversion
4941 // we simple return the result and let the converted string
4942 // become garbage; there is no reason to keep two identical strings
4943 // alive.
4944 return s;
4945 }
4946}
4947
4948
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004949namespace {
4950
4951struct ToLowerTraits {
4952 typedef unibrow::ToLowercase UnibrowConverter;
4953
4954 static bool ConvertAscii(char* dst, char* src, int length) {
4955 bool changed = false;
4956 for (int i = 0; i < length; ++i) {
4957 char c = src[i];
4958 if ('A' <= c && c <= 'Z') {
4959 c += ('a' - 'A');
4960 changed = true;
4961 }
4962 dst[i] = c;
4963 }
4964 return changed;
4965 }
4966};
4967
4968
4969struct ToUpperTraits {
4970 typedef unibrow::ToUppercase UnibrowConverter;
4971
4972 static bool ConvertAscii(char* dst, char* src, int length) {
4973 bool changed = false;
4974 for (int i = 0; i < length; ++i) {
4975 char c = src[i];
4976 if ('a' <= c && c <= 'z') {
4977 c -= ('a' - 'A');
4978 changed = true;
4979 }
4980 dst[i] = c;
4981 }
4982 return changed;
4983 }
4984};
4985
4986} // namespace
4987
4988
4989template <typename ConvertTraits>
4990static Object* ConvertCase(
4991 Arguments args,
4992 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004993 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004994 CONVERT_CHECKED(String, s, args[0]);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00004995 s = s->TryFlattenGetString();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004996
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004997 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004998 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004999 if (length == 0) return s;
5000
5001 // Simpler handling of ascii strings.
5002 //
5003 // NOTE: This assumes that the upper/lower case of an ascii
5004 // character is also ascii. This is currently the case, but it
5005 // might break in the future if we implement more context and locale
5006 // dependent upper/lower conversions.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005007 if (s->IsSeqAsciiString()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005008 Object* o = Heap::AllocateRawAsciiString(length);
5009 if (o->IsFailure()) return o;
5010 SeqAsciiString* result = SeqAsciiString::cast(o);
5011 bool has_changed_character = ConvertTraits::ConvertAscii(
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005012 result->GetChars(), SeqAsciiString::cast(s)->GetChars(), length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005013 return has_changed_character ? result : s;
5014 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005015
5016 Object* answer = ConvertCaseHelper(s, length, length, mapping);
5017 if (answer->IsSmi()) {
5018 // Retry with correct length.
5019 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
5020 }
5021 return answer; // This may be a failure.
5022}
5023
5024
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005025static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005026 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005027}
5028
5029
5030static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005031 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005032}
5033
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005034
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005035static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5036 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5037}
5038
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005039
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005040static Object* Runtime_StringTrim(Arguments args) {
5041 NoHandleAllocation ha;
5042 ASSERT(args.length() == 3);
5043
5044 CONVERT_CHECKED(String, s, args[0]);
5045 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5046 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5047
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005048 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005049 int length = s->length();
5050
5051 int left = 0;
5052 if (trimLeft) {
5053 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5054 left++;
5055 }
5056 }
5057
5058 int right = length;
5059 if (trimRight) {
5060 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5061 right--;
5062 }
5063 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005064 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005065}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005066
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005067
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005068template <typename schar, typename pchar>
5069void FindStringIndices(Vector<const schar> subject,
5070 Vector<const pchar> pattern,
5071 ZoneList<int>* indices,
5072 unsigned int limit) {
5073 ASSERT(limit > 0);
5074 // Collect indices of pattern in subject, and the end-of-string index.
5075 // Stop after finding at most limit values.
5076 StringSearchStrategy strategy =
5077 InitializeStringSearch(pattern, sizeof(schar) == 1);
5078 switch (strategy) {
5079 case SEARCH_FAIL: return;
5080 case SEARCH_SHORT: {
5081 int pattern_length = pattern.length();
5082 int index = 0;
5083 while (limit > 0) {
5084 index = SimpleIndexOf(subject, pattern, index);
5085 if (index < 0) return;
5086 indices->Add(index);
5087 index += pattern_length;
5088 limit--;
5089 }
5090 return;
5091 }
5092 case SEARCH_LONG: {
5093 int pattern_length = pattern.length();
5094 int index = 0;
5095 while (limit > 0) {
5096 index = ComplexIndexOf(subject, pattern, index);
5097 if (index < 0) return;
5098 indices->Add(index);
5099 index += pattern_length;
5100 limit--;
5101 }
5102 return;
5103 }
5104 default:
5105 UNREACHABLE();
5106 return;
5107 }
5108}
5109
5110template <typename schar>
5111inline void FindCharIndices(Vector<const schar> subject,
5112 const schar pattern_char,
5113 ZoneList<int>* indices,
5114 unsigned int limit) {
5115 // Collect indices of pattern_char in subject, and the end-of-string index.
5116 // Stop after finding at most limit values.
5117 int index = 0;
5118 while (limit > 0) {
5119 index = SingleCharIndexOf(subject, pattern_char, index);
5120 if (index < 0) return;
5121 indices->Add(index);
5122 index++;
5123 limit--;
5124 }
5125}
5126
5127
5128static Object* Runtime_StringSplit(Arguments args) {
5129 ASSERT(args.length() == 3);
5130 HandleScope handle_scope;
5131 CONVERT_ARG_CHECKED(String, subject, 0);
5132 CONVERT_ARG_CHECKED(String, pattern, 1);
5133 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5134
5135 int subject_length = subject->length();
5136 int pattern_length = pattern->length();
5137 RUNTIME_ASSERT(pattern_length > 0);
5138
5139 // The limit can be very large (0xffffffffu), but since the pattern
5140 // isn't empty, we can never create more parts than ~half the length
5141 // of the subject.
5142
5143 if (!subject->IsFlat()) FlattenString(subject);
5144
5145 static const int kMaxInitialListCapacity = 16;
5146
5147 ZoneScope scope(DELETE_ON_EXIT);
5148
5149 // Find (up to limit) indices of separator and end-of-string in subject
5150 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5151 ZoneList<int> indices(initial_capacity);
5152 if (pattern_length == 1) {
5153 // Special case, go directly to fast single-character split.
5154 AssertNoAllocation nogc;
5155 uc16 pattern_char = pattern->Get(0);
5156 if (subject->IsTwoByteRepresentation()) {
5157 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5158 &indices,
5159 limit);
5160 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5161 FindCharIndices(subject->ToAsciiVector(),
5162 static_cast<char>(pattern_char),
5163 &indices,
5164 limit);
5165 }
5166 } else {
5167 if (!pattern->IsFlat()) FlattenString(pattern);
5168 AssertNoAllocation nogc;
5169 if (subject->IsAsciiRepresentation()) {
5170 Vector<const char> subject_vector = subject->ToAsciiVector();
5171 if (pattern->IsAsciiRepresentation()) {
5172 FindStringIndices(subject_vector,
5173 pattern->ToAsciiVector(),
5174 &indices,
5175 limit);
5176 } else {
5177 FindStringIndices(subject_vector,
5178 pattern->ToUC16Vector(),
5179 &indices,
5180 limit);
5181 }
5182 } else {
5183 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5184 if (pattern->IsAsciiRepresentation()) {
5185 FindStringIndices(subject_vector,
5186 pattern->ToAsciiVector(),
5187 &indices,
5188 limit);
5189 } else {
5190 FindStringIndices(subject_vector,
5191 pattern->ToUC16Vector(),
5192 &indices,
5193 limit);
5194 }
5195 }
5196 }
5197 if (static_cast<uint32_t>(indices.length()) < limit) {
5198 indices.Add(subject_length);
5199 }
5200 // The list indices now contains the end of each part to create.
5201
5202
5203 // Create JSArray of substrings separated by separator.
5204 int part_count = indices.length();
5205
5206 Handle<JSArray> result = Factory::NewJSArray(part_count);
5207 result->set_length(Smi::FromInt(part_count));
5208
5209 ASSERT(result->HasFastElements());
5210
5211 if (part_count == 1 && indices.at(0) == subject_length) {
5212 FixedArray::cast(result->elements())->set(0, *subject);
5213 return *result;
5214 }
5215
5216 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5217 int part_start = 0;
5218 for (int i = 0; i < part_count; i++) {
5219 HandleScope local_loop_handle;
5220 int part_end = indices.at(i);
5221 Handle<String> substring =
5222 Factory::NewSubString(subject, part_start, part_end);
5223 elements->set(i, *substring);
5224 part_start = part_end + pattern_length;
5225 }
5226
5227 return *result;
5228}
5229
5230
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005231// Copies ascii characters to the given fixed array looking up
5232// one-char strings in the cache. Gives up on the first char that is
5233// not in the cache and fills the remainder with smi zeros. Returns
5234// the length of the successfully copied prefix.
5235static int CopyCachedAsciiCharsToArray(const char* chars,
5236 FixedArray* elements,
5237 int length) {
5238 AssertNoAllocation nogc;
5239 FixedArray* ascii_cache = Heap::single_character_string_cache();
5240 Object* undefined = Heap::undefined_value();
5241 int i;
5242 for (i = 0; i < length; ++i) {
5243 Object* value = ascii_cache->get(chars[i]);
5244 if (value == undefined) break;
5245 ASSERT(!Heap::InNewSpace(value));
5246 elements->set(i, value, SKIP_WRITE_BARRIER);
5247 }
5248 if (i < length) {
5249 ASSERT(Smi::FromInt(0) == 0);
5250 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5251 }
5252#ifdef DEBUG
5253 for (int j = 0; j < length; ++j) {
5254 Object* element = elements->get(j);
5255 ASSERT(element == Smi::FromInt(0) ||
5256 (element->IsString() && String::cast(element)->LooksValid()));
5257 }
5258#endif
5259 return i;
5260}
5261
5262
5263// Converts a String to JSArray.
5264// For example, "foo" => ["f", "o", "o"].
5265static Object* Runtime_StringToArray(Arguments args) {
5266 HandleScope scope;
5267 ASSERT(args.length() == 1);
5268 CONVERT_ARG_CHECKED(String, s, 0);
5269
5270 s->TryFlatten();
5271 const int length = s->length();
5272
5273 Handle<FixedArray> elements;
5274 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5275 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5276 if (obj->IsFailure()) return obj;
5277 elements = Handle<FixedArray>(FixedArray::cast(obj));
5278
5279 Vector<const char> chars = s->ToAsciiVector();
5280 // Note, this will initialize all elements (not only the prefix)
5281 // to prevent GC from seeing partially initialized array.
5282 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5283 *elements,
5284 length);
5285
5286 for (int i = num_copied_from_cache; i < length; ++i) {
5287 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5288 }
5289 } else {
5290 elements = Factory::NewFixedArray(length);
5291 for (int i = 0; i < length; ++i) {
5292 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5293 }
5294 }
5295
5296#ifdef DEBUG
5297 for (int i = 0; i < length; ++i) {
5298 ASSERT(String::cast(elements->get(i))->length() == 1);
5299 }
5300#endif
5301
5302 return *Factory::NewJSArrayWithElements(elements);
5303}
5304
5305
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005306bool Runtime::IsUpperCaseChar(uint16_t ch) {
5307 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5308 int char_length = to_upper_mapping.get(ch, 0, chars);
5309 return char_length == 0;
5310}
5311
5312
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005313static Object* Runtime_NumberToString(Arguments args) {
5314 NoHandleAllocation ha;
5315 ASSERT(args.length() == 1);
5316
5317 Object* number = args[0];
5318 RUNTIME_ASSERT(number->IsNumber());
5319
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005320 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005321}
5322
5323
ager@chromium.org357bf652010-04-12 11:30:10 +00005324static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5325 NoHandleAllocation ha;
5326 ASSERT(args.length() == 1);
5327
5328 Object* number = args[0];
5329 RUNTIME_ASSERT(number->IsNumber());
5330
5331 return Heap::NumberToString(number, false);
5332}
5333
5334
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005335static Object* Runtime_NumberToInteger(Arguments args) {
5336 NoHandleAllocation ha;
5337 ASSERT(args.length() == 1);
5338
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005339 CONVERT_DOUBLE_CHECKED(number, args[0]);
5340
5341 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5342 if (number > 0 && number <= Smi::kMaxValue) {
5343 return Smi::FromInt(static_cast<int>(number));
5344 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005345 return Heap::NumberFromDouble(DoubleToInteger(number));
5346}
5347
5348
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00005349
5350
5351
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005352static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
5353 NoHandleAllocation ha;
5354 ASSERT(args.length() == 1);
5355
5356 CONVERT_DOUBLE_CHECKED(number, args[0]);
5357
5358 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5359 if (number > 0 && number <= Smi::kMaxValue) {
5360 return Smi::FromInt(static_cast<int>(number));
5361 }
5362
5363 double double_value = DoubleToInteger(number);
5364 // Map both -0 and +0 to +0.
5365 if (double_value == 0) double_value = 0;
5366
5367 return Heap::NumberFromDouble(double_value);
5368}
5369
5370
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005371static Object* Runtime_NumberToJSUint32(Arguments args) {
5372 NoHandleAllocation ha;
5373 ASSERT(args.length() == 1);
5374
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005375 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005376 return Heap::NumberFromUint32(number);
5377}
5378
5379
5380static Object* Runtime_NumberToJSInt32(Arguments args) {
5381 NoHandleAllocation ha;
5382 ASSERT(args.length() == 1);
5383
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005384 CONVERT_DOUBLE_CHECKED(number, args[0]);
5385
5386 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5387 if (number > 0 && number <= Smi::kMaxValue) {
5388 return Smi::FromInt(static_cast<int>(number));
5389 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005390 return Heap::NumberFromInt32(DoubleToInt32(number));
5391}
5392
5393
ager@chromium.org870a0b62008-11-04 11:43:05 +00005394// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5395// a small integer.
5396static Object* Runtime_NumberToSmi(Arguments args) {
5397 NoHandleAllocation ha;
5398 ASSERT(args.length() == 1);
5399
5400 Object* obj = args[0];
5401 if (obj->IsSmi()) {
5402 return obj;
5403 }
5404 if (obj->IsHeapNumber()) {
5405 double value = HeapNumber::cast(obj)->value();
5406 int int_value = FastD2I(value);
5407 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5408 return Smi::FromInt(int_value);
5409 }
5410 }
5411 return Heap::nan_value();
5412}
5413
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005414
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005415static Object* Runtime_NumberAdd(Arguments args) {
5416 NoHandleAllocation ha;
5417 ASSERT(args.length() == 2);
5418
5419 CONVERT_DOUBLE_CHECKED(x, args[0]);
5420 CONVERT_DOUBLE_CHECKED(y, args[1]);
5421 return Heap::AllocateHeapNumber(x + y);
5422}
5423
5424
5425static Object* Runtime_NumberSub(Arguments args) {
5426 NoHandleAllocation ha;
5427 ASSERT(args.length() == 2);
5428
5429 CONVERT_DOUBLE_CHECKED(x, args[0]);
5430 CONVERT_DOUBLE_CHECKED(y, args[1]);
5431 return Heap::AllocateHeapNumber(x - y);
5432}
5433
5434
5435static Object* Runtime_NumberMul(Arguments args) {
5436 NoHandleAllocation ha;
5437 ASSERT(args.length() == 2);
5438
5439 CONVERT_DOUBLE_CHECKED(x, args[0]);
5440 CONVERT_DOUBLE_CHECKED(y, args[1]);
5441 return Heap::AllocateHeapNumber(x * y);
5442}
5443
5444
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005445static Object* Runtime_NumberUnaryMinus(Arguments args) {
5446 NoHandleAllocation ha;
5447 ASSERT(args.length() == 1);
5448
5449 CONVERT_DOUBLE_CHECKED(x, args[0]);
5450 return Heap::AllocateHeapNumber(-x);
5451}
5452
5453
5454static Object* Runtime_NumberDiv(Arguments args) {
5455 NoHandleAllocation ha;
5456 ASSERT(args.length() == 2);
5457
5458 CONVERT_DOUBLE_CHECKED(x, args[0]);
5459 CONVERT_DOUBLE_CHECKED(y, args[1]);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005460 return Heap::NumberFromDouble(x / y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005461}
5462
5463
5464static Object* Runtime_NumberMod(Arguments args) {
5465 NoHandleAllocation ha;
5466 ASSERT(args.length() == 2);
5467
5468 CONVERT_DOUBLE_CHECKED(x, args[0]);
5469 CONVERT_DOUBLE_CHECKED(y, args[1]);
5470
ager@chromium.org3811b432009-10-28 14:53:37 +00005471 x = modulo(x, y);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005472 // NumberFromDouble may return a Smi instead of a Number object
5473 return Heap::NumberFromDouble(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005474}
5475
5476
5477static Object* Runtime_StringAdd(Arguments args) {
5478 NoHandleAllocation ha;
5479 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005480 CONVERT_CHECKED(String, str1, args[0]);
5481 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005482 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005483 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005484}
5485
5486
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005487template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005488static inline void StringBuilderConcatHelper(String* special,
5489 sinkchar* sink,
5490 FixedArray* fixed_array,
5491 int array_length) {
5492 int position = 0;
5493 for (int i = 0; i < array_length; i++) {
5494 Object* element = fixed_array->get(i);
5495 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005496 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005497 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005498 int pos;
5499 int len;
5500 if (encoded_slice > 0) {
5501 // Position and length encoded in one smi.
5502 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5503 len = StringBuilderSubstringLength::decode(encoded_slice);
5504 } else {
5505 // Position and length encoded in two smis.
5506 Object* obj = fixed_array->get(++i);
5507 ASSERT(obj->IsSmi());
5508 pos = Smi::cast(obj)->value();
5509 len = -encoded_slice;
5510 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005511 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005512 sink + position,
5513 pos,
5514 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005515 position += len;
5516 } else {
5517 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005518 int element_length = string->length();
5519 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005520 position += element_length;
5521 }
5522 }
5523}
5524
5525
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005526static Object* Runtime_StringBuilderConcat(Arguments args) {
5527 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005528 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005529 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005530 if (!args[1]->IsSmi()) {
5531 Top::context()->mark_out_of_memory();
5532 return Failure::OutOfMemoryException();
5533 }
5534 int array_length = Smi::cast(args[1])->value();
5535 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005536
5537 // This assumption is used by the slice encoding in one or two smis.
5538 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5539
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005540 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005541 if (!array->HasFastElements()) {
5542 return Top::Throw(Heap::illegal_argument_symbol());
5543 }
5544 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005545 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005546 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005547 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548
5549 if (array_length == 0) {
5550 return Heap::empty_string();
5551 } else if (array_length == 1) {
5552 Object* first = fixed_array->get(0);
5553 if (first->IsString()) return first;
5554 }
5555
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005556 bool ascii = special->HasOnlyAsciiChars();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005557 int position = 0;
5558 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005559 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005560 Object* elt = fixed_array->get(i);
5561 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005562 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005563 int smi_value = Smi::cast(elt)->value();
5564 int pos;
5565 int len;
5566 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005567 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005568 pos = StringBuilderSubstringPosition::decode(smi_value);
5569 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005570 } else {
5571 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005572 len = -smi_value;
5573 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005574 i++;
5575 if (i >= array_length) {
5576 return Top::Throw(Heap::illegal_argument_symbol());
5577 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005578 Object* next_smi = fixed_array->get(i);
5579 if (!next_smi->IsSmi()) {
5580 return Top::Throw(Heap::illegal_argument_symbol());
5581 }
5582 pos = Smi::cast(next_smi)->value();
5583 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005584 return Top::Throw(Heap::illegal_argument_symbol());
5585 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005587 ASSERT(pos >= 0);
5588 ASSERT(len >= 0);
5589 if (pos > special_length || len > special_length - pos) {
5590 return Top::Throw(Heap::illegal_argument_symbol());
5591 }
5592 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005593 } else if (elt->IsString()) {
5594 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005595 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005596 increment = element_length;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00005597 if (ascii && !element->HasOnlyAsciiChars()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005598 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005599 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005600 } else {
5601 return Top::Throw(Heap::illegal_argument_symbol());
5602 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005603 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005604 Top::context()->mark_out_of_memory();
5605 return Failure::OutOfMemoryException();
5606 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005607 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005608 }
5609
5610 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005611 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005612
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005613 if (ascii) {
5614 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005615 if (object->IsFailure()) return object;
5616 SeqAsciiString* answer = SeqAsciiString::cast(object);
5617 StringBuilderConcatHelper(special,
5618 answer->GetChars(),
5619 fixed_array,
5620 array_length);
5621 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005622 } else {
5623 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005624 if (object->IsFailure()) return object;
5625 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5626 StringBuilderConcatHelper(special,
5627 answer->GetChars(),
5628 fixed_array,
5629 array_length);
5630 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005631 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005632}
5633
5634
5635static Object* Runtime_NumberOr(Arguments args) {
5636 NoHandleAllocation ha;
5637 ASSERT(args.length() == 2);
5638
5639 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5640 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5641 return Heap::NumberFromInt32(x | y);
5642}
5643
5644
5645static Object* Runtime_NumberAnd(Arguments args) {
5646 NoHandleAllocation ha;
5647 ASSERT(args.length() == 2);
5648
5649 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5650 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5651 return Heap::NumberFromInt32(x & y);
5652}
5653
5654
5655static Object* Runtime_NumberXor(Arguments args) {
5656 NoHandleAllocation ha;
5657 ASSERT(args.length() == 2);
5658
5659 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5660 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5661 return Heap::NumberFromInt32(x ^ y);
5662}
5663
5664
5665static Object* Runtime_NumberNot(Arguments args) {
5666 NoHandleAllocation ha;
5667 ASSERT(args.length() == 1);
5668
5669 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5670 return Heap::NumberFromInt32(~x);
5671}
5672
5673
5674static Object* Runtime_NumberShl(Arguments args) {
5675 NoHandleAllocation ha;
5676 ASSERT(args.length() == 2);
5677
5678 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5679 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5680 return Heap::NumberFromInt32(x << (y & 0x1f));
5681}
5682
5683
5684static Object* Runtime_NumberShr(Arguments args) {
5685 NoHandleAllocation ha;
5686 ASSERT(args.length() == 2);
5687
5688 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5689 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5690 return Heap::NumberFromUint32(x >> (y & 0x1f));
5691}
5692
5693
5694static Object* Runtime_NumberSar(Arguments args) {
5695 NoHandleAllocation ha;
5696 ASSERT(args.length() == 2);
5697
5698 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5699 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5700 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5701}
5702
5703
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005704static Object* Runtime_NumberEquals(Arguments args) {
5705 NoHandleAllocation ha;
5706 ASSERT(args.length() == 2);
5707
5708 CONVERT_DOUBLE_CHECKED(x, args[0]);
5709 CONVERT_DOUBLE_CHECKED(y, args[1]);
5710 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5711 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5712 if (x == y) return Smi::FromInt(EQUAL);
5713 Object* result;
5714 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5715 result = Smi::FromInt(EQUAL);
5716 } else {
5717 result = Smi::FromInt(NOT_EQUAL);
5718 }
5719 return result;
5720}
5721
5722
5723static Object* Runtime_StringEquals(Arguments args) {
5724 NoHandleAllocation ha;
5725 ASSERT(args.length() == 2);
5726
5727 CONVERT_CHECKED(String, x, args[0]);
5728 CONVERT_CHECKED(String, y, args[1]);
5729
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005730 bool not_equal = !x->Equals(y);
5731 // This is slightly convoluted because the value that signifies
5732 // equality is 0 and inequality is 1 so we have to negate the result
5733 // from String::Equals.
5734 ASSERT(not_equal == 0 || not_equal == 1);
5735 STATIC_CHECK(EQUAL == 0);
5736 STATIC_CHECK(NOT_EQUAL == 1);
5737 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005738}
5739
5740
5741static Object* Runtime_NumberCompare(Arguments args) {
5742 NoHandleAllocation ha;
5743 ASSERT(args.length() == 3);
5744
5745 CONVERT_DOUBLE_CHECKED(x, args[0]);
5746 CONVERT_DOUBLE_CHECKED(y, args[1]);
5747 if (isnan(x) || isnan(y)) return args[2];
5748 if (x == y) return Smi::FromInt(EQUAL);
5749 if (isless(x, y)) return Smi::FromInt(LESS);
5750 return Smi::FromInt(GREATER);
5751}
5752
5753
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005754// Compare two Smis as if they were converted to strings and then
5755// compared lexicographically.
5756static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5757 NoHandleAllocation ha;
5758 ASSERT(args.length() == 2);
5759
5760 // Arrays for the individual characters of the two Smis. Smis are
5761 // 31 bit integers and 10 decimal digits are therefore enough.
5762 static int x_elms[10];
5763 static int y_elms[10];
5764
5765 // Extract the integer values from the Smis.
5766 CONVERT_CHECKED(Smi, x, args[0]);
5767 CONVERT_CHECKED(Smi, y, args[1]);
5768 int x_value = x->value();
5769 int y_value = y->value();
5770
5771 // If the integers are equal so are the string representations.
5772 if (x_value == y_value) return Smi::FromInt(EQUAL);
5773
5774 // If one of the integers are zero the normal integer order is the
5775 // same as the lexicographic order of the string representations.
5776 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5777
ager@chromium.org32912102009-01-16 10:38:43 +00005778 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005779 // smallest because the char code of '-' is less than the char code
5780 // of any digit. Otherwise, we make both values positive.
5781 if (x_value < 0 || y_value < 0) {
5782 if (y_value >= 0) return Smi::FromInt(LESS);
5783 if (x_value >= 0) return Smi::FromInt(GREATER);
5784 x_value = -x_value;
5785 y_value = -y_value;
5786 }
5787
5788 // Convert the integers to arrays of their decimal digits.
5789 int x_index = 0;
5790 int y_index = 0;
5791 while (x_value > 0) {
5792 x_elms[x_index++] = x_value % 10;
5793 x_value /= 10;
5794 }
5795 while (y_value > 0) {
5796 y_elms[y_index++] = y_value % 10;
5797 y_value /= 10;
5798 }
5799
5800 // Loop through the arrays of decimal digits finding the first place
5801 // where they differ.
5802 while (--x_index >= 0 && --y_index >= 0) {
5803 int diff = x_elms[x_index] - y_elms[y_index];
5804 if (diff != 0) return Smi::FromInt(diff);
5805 }
5806
5807 // If one array is a suffix of the other array, the longest array is
5808 // the representation of the largest of the Smis in the
5809 // lexicographic ordering.
5810 return Smi::FromInt(x_index - y_index);
5811}
5812
5813
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005814static Object* StringInputBufferCompare(String* x, String* y) {
5815 static StringInputBuffer bufx;
5816 static StringInputBuffer bufy;
5817 bufx.Reset(x);
5818 bufy.Reset(y);
5819 while (bufx.has_more() && bufy.has_more()) {
5820 int d = bufx.GetNext() - bufy.GetNext();
5821 if (d < 0) return Smi::FromInt(LESS);
5822 else if (d > 0) return Smi::FromInt(GREATER);
5823 }
5824
5825 // x is (non-trivial) prefix of y:
5826 if (bufy.has_more()) return Smi::FromInt(LESS);
5827 // y is prefix of x:
5828 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5829}
5830
5831
5832static Object* FlatStringCompare(String* x, String* y) {
5833 ASSERT(x->IsFlat());
5834 ASSERT(y->IsFlat());
5835 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5836 int prefix_length = x->length();
5837 if (y->length() < prefix_length) {
5838 prefix_length = y->length();
5839 equal_prefix_result = Smi::FromInt(GREATER);
5840 } else if (y->length() > prefix_length) {
5841 equal_prefix_result = Smi::FromInt(LESS);
5842 }
5843 int r;
5844 if (x->IsAsciiRepresentation()) {
5845 Vector<const char> x_chars = x->ToAsciiVector();
5846 if (y->IsAsciiRepresentation()) {
5847 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005848 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005849 } else {
5850 Vector<const uc16> y_chars = y->ToUC16Vector();
5851 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5852 }
5853 } else {
5854 Vector<const uc16> x_chars = x->ToUC16Vector();
5855 if (y->IsAsciiRepresentation()) {
5856 Vector<const char> y_chars = y->ToAsciiVector();
5857 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5858 } else {
5859 Vector<const uc16> y_chars = y->ToUC16Vector();
5860 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5861 }
5862 }
5863 Object* result;
5864 if (r == 0) {
5865 result = equal_prefix_result;
5866 } else {
5867 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5868 }
5869 ASSERT(result == StringInputBufferCompare(x, y));
5870 return result;
5871}
5872
5873
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005874static Object* Runtime_StringCompare(Arguments args) {
5875 NoHandleAllocation ha;
5876 ASSERT(args.length() == 2);
5877
5878 CONVERT_CHECKED(String, x, args[0]);
5879 CONVERT_CHECKED(String, y, args[1]);
5880
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005881 Counters::string_compare_runtime.Increment();
5882
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005883 // A few fast case tests before we flatten.
5884 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005885 if (y->length() == 0) {
5886 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005888 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005889 return Smi::FromInt(LESS);
5890 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005891
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005892 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005893 if (d < 0) return Smi::FromInt(LESS);
5894 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005895
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005896 Object* obj = Heap::PrepareForCompare(x);
5897 if (obj->IsFailure()) return obj;
5898 obj = Heap::PrepareForCompare(y);
5899 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005900
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005901 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5902 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005903}
5904
5905
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005906static Object* Runtime_Math_acos(Arguments args) {
5907 NoHandleAllocation ha;
5908 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005909 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005910
5911 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005912 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005913}
5914
5915
5916static Object* Runtime_Math_asin(Arguments args) {
5917 NoHandleAllocation ha;
5918 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005919 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005920
5921 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005922 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005923}
5924
5925
5926static Object* Runtime_Math_atan(Arguments args) {
5927 NoHandleAllocation ha;
5928 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005929 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005930
5931 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005932 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005933}
5934
5935
5936static Object* Runtime_Math_atan2(Arguments args) {
5937 NoHandleAllocation ha;
5938 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005939 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005940
5941 CONVERT_DOUBLE_CHECKED(x, args[0]);
5942 CONVERT_DOUBLE_CHECKED(y, args[1]);
5943 double result;
5944 if (isinf(x) && isinf(y)) {
5945 // Make sure that the result in case of two infinite arguments
5946 // is a multiple of Pi / 4. The sign of the result is determined
5947 // by the first argument (x) and the sign of the second argument
5948 // determines the multiplier: one or three.
5949 static double kPiDividedBy4 = 0.78539816339744830962;
5950 int multiplier = (x < 0) ? -1 : 1;
5951 if (y < 0) multiplier *= 3;
5952 result = multiplier * kPiDividedBy4;
5953 } else {
5954 result = atan2(x, y);
5955 }
5956 return Heap::AllocateHeapNumber(result);
5957}
5958
5959
5960static Object* Runtime_Math_ceil(Arguments args) {
5961 NoHandleAllocation ha;
5962 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005963 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005964
5965 CONVERT_DOUBLE_CHECKED(x, args[0]);
5966 return Heap::NumberFromDouble(ceiling(x));
5967}
5968
5969
5970static Object* Runtime_Math_cos(Arguments args) {
5971 NoHandleAllocation ha;
5972 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005973 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005974
5975 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005976 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005977}
5978
5979
5980static Object* Runtime_Math_exp(Arguments args) {
5981 NoHandleAllocation ha;
5982 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005983 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005984
5985 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005986 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005987}
5988
5989
5990static Object* Runtime_Math_floor(Arguments args) {
5991 NoHandleAllocation ha;
5992 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005993 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005994
5995 CONVERT_DOUBLE_CHECKED(x, args[0]);
5996 return Heap::NumberFromDouble(floor(x));
5997}
5998
5999
6000static Object* Runtime_Math_log(Arguments args) {
6001 NoHandleAllocation ha;
6002 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006003 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006004
6005 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006006 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006007}
6008
6009
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006010// Helper function to compute x^y, where y is known to be an
6011// integer. Uses binary decomposition to limit the number of
6012// multiplications; see the discussion in "Hacker's Delight" by Henry
6013// S. Warren, Jr., figure 11-6, page 213.
6014static double powi(double x, int y) {
6015 ASSERT(y != kMinInt);
6016 unsigned n = (y < 0) ? -y : y;
6017 double m = x;
6018 double p = 1;
6019 while (true) {
6020 if ((n & 1) != 0) p *= m;
6021 n >>= 1;
6022 if (n == 0) {
6023 if (y < 0) {
6024 // Unfortunately, we have to be careful when p has reached
6025 // infinity in the computation, because sometimes the higher
6026 // internal precision in the pow() implementation would have
6027 // given us a finite p. This happens very rarely.
6028 double result = 1.0 / p;
6029 return (result == 0 && isinf(p))
6030 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
6031 : result;
6032 } else {
6033 return p;
6034 }
6035 }
6036 m *= m;
6037 }
6038}
6039
6040
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006041static Object* Runtime_Math_pow(Arguments args) {
6042 NoHandleAllocation ha;
6043 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006044 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006045
6046 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006047
6048 // If the second argument is a smi, it is much faster to call the
6049 // custom powi() function than the generic pow().
6050 if (args[1]->IsSmi()) {
6051 int y = Smi::cast(args[1])->value();
6052 return Heap::AllocateHeapNumber(powi(x, y));
6053 }
6054
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006055 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00006056
6057 if (!isinf(x)) {
6058 if (y == 0.5) {
6059 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6060 // square root of a number. To speed up such computations, we
6061 // explictly check for this case and use the sqrt() function
6062 // which is faster than pow().
6063 return Heap::AllocateHeapNumber(sqrt(x));
6064 } else if (y == -0.5) {
6065 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6066 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6067 }
6068 }
6069
6070 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006071 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006072 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6073 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006074 } else {
6075 return Heap::AllocateHeapNumber(pow(x, y));
6076 }
6077}
6078
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006079// Fast version of Math.pow if we know that y is not an integer and
6080// y is not -0.5 or 0.5. Used as slowcase from codegen.
6081static Object* Runtime_Math_pow_cfunction(Arguments args) {
6082 NoHandleAllocation ha;
6083 ASSERT(args.length() == 2);
6084 CONVERT_DOUBLE_CHECKED(x, args[0]);
6085 CONVERT_DOUBLE_CHECKED(y, args[1]);
6086 if (y == 0) {
6087 return Smi::FromInt(1);
6088 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6089 return Heap::nan_value();
6090 } else {
6091 return Heap::AllocateHeapNumber(pow(x, y));
6092 }
6093}
6094
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006095
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006096static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006097 NoHandleAllocation ha;
6098 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006099 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006100
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006101 if (!args[0]->IsHeapNumber()) {
6102 // Must be smi. Return the argument unchanged for all the other types
6103 // to make fuzz-natives test happy.
6104 return args[0];
6105 }
6106
6107 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6108
6109 double value = number->value();
6110 int exponent = number->get_exponent();
6111 int sign = number->get_sign();
6112
6113 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6114 // should be rounded to 2^30, which is not smi.
6115 if (!sign && exponent <= kSmiValueSize - 3) {
6116 return Smi::FromInt(static_cast<int>(value + 0.5));
6117 }
6118
6119 // If the magnitude is big enough, there's no place for fraction part. If we
6120 // try to add 0.5 to this number, 1.0 will be added instead.
6121 if (exponent >= 52) {
6122 return number;
6123 }
6124
6125 if (sign && value >= -0.5) return Heap::minus_zero_value();
6126
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006127 // Do not call NumberFromDouble() to avoid extra checks.
6128 return Heap::AllocateHeapNumber(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006129}
6130
6131
6132static Object* Runtime_Math_sin(Arguments args) {
6133 NoHandleAllocation ha;
6134 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006135 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006136
6137 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006138 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006139}
6140
6141
6142static Object* Runtime_Math_sqrt(Arguments args) {
6143 NoHandleAllocation ha;
6144 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006145 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006146
6147 CONVERT_DOUBLE_CHECKED(x, args[0]);
6148 return Heap::AllocateHeapNumber(sqrt(x));
6149}
6150
6151
6152static Object* Runtime_Math_tan(Arguments args) {
6153 NoHandleAllocation ha;
6154 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006155 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006156
6157 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006158 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006159}
6160
6161
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006162static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006163 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6164 181, 212, 243, 273, 304, 334};
6165 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6166 182, 213, 244, 274, 305, 335};
6167
6168 year += month / 12;
6169 month %= 12;
6170 if (month < 0) {
6171 year--;
6172 month += 12;
6173 }
6174
6175 ASSERT(month >= 0);
6176 ASSERT(month < 12);
6177
6178 // year_delta is an arbitrary number such that:
6179 // a) year_delta = -1 (mod 400)
6180 // b) year + year_delta > 0 for years in the range defined by
6181 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6182 // Jan 1 1970. This is required so that we don't run into integer
6183 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006184 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006185 // operations.
6186 static const int year_delta = 399999;
6187 static const int base_day = 365 * (1970 + year_delta) +
6188 (1970 + year_delta) / 4 -
6189 (1970 + year_delta) / 100 +
6190 (1970 + year_delta) / 400;
6191
6192 int year1 = year + year_delta;
6193 int day_from_year = 365 * year1 +
6194 year1 / 4 -
6195 year1 / 100 +
6196 year1 / 400 -
6197 base_day;
6198
6199 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006200 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006201 }
6202
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006203 return day_from_year + day_from_month_leap[month] + day - 1;
6204}
6205
6206
6207static Object* Runtime_DateMakeDay(Arguments args) {
6208 NoHandleAllocation ha;
6209 ASSERT(args.length() == 3);
6210
6211 CONVERT_SMI_CHECKED(year, args[0]);
6212 CONVERT_SMI_CHECKED(month, args[1]);
6213 CONVERT_SMI_CHECKED(date, args[2]);
6214
6215 return Smi::FromInt(MakeDay(year, month, date));
6216}
6217
6218
6219static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6220static const int kDaysIn4Years = 4 * 365 + 1;
6221static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6222static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6223static const int kDays1970to2000 = 30 * 365 + 7;
6224static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6225 kDays1970to2000;
6226static const int kYearsOffset = 400000;
6227
6228static const char kDayInYear[] = {
6229 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6230 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6231 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6232 22, 23, 24, 25, 26, 27, 28,
6233 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6234 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6235 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6236 22, 23, 24, 25, 26, 27, 28, 29, 30,
6237 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6238 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6239 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6240 22, 23, 24, 25, 26, 27, 28, 29, 30,
6241 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6242 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6243 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6244 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6245 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6246 22, 23, 24, 25, 26, 27, 28, 29, 30,
6247 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6248 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6249 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6250 22, 23, 24, 25, 26, 27, 28, 29, 30,
6251 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6252 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6253
6254 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6255 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6256 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6257 22, 23, 24, 25, 26, 27, 28,
6258 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6259 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6260 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6261 22, 23, 24, 25, 26, 27, 28, 29, 30,
6262 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6263 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6264 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6265 22, 23, 24, 25, 26, 27, 28, 29, 30,
6266 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6267 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6268 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6269 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6270 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6271 22, 23, 24, 25, 26, 27, 28, 29, 30,
6272 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6273 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6274 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6275 22, 23, 24, 25, 26, 27, 28, 29, 30,
6276 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6277 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6278
6279 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6280 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6281 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6282 22, 23, 24, 25, 26, 27, 28, 29,
6283 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6284 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6285 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6286 22, 23, 24, 25, 26, 27, 28, 29, 30,
6287 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6288 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6289 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6290 22, 23, 24, 25, 26, 27, 28, 29, 30,
6291 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6292 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6293 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6294 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6295 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6296 22, 23, 24, 25, 26, 27, 28, 29, 30,
6297 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6298 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6299 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6300 22, 23, 24, 25, 26, 27, 28, 29, 30,
6301 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6302 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6303
6304 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6305 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6306 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6307 22, 23, 24, 25, 26, 27, 28,
6308 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6309 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6310 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6311 22, 23, 24, 25, 26, 27, 28, 29, 30,
6312 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6313 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6314 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6315 22, 23, 24, 25, 26, 27, 28, 29, 30,
6316 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6317 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6318 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6319 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6320 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6321 22, 23, 24, 25, 26, 27, 28, 29, 30,
6322 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6323 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6324 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6325 22, 23, 24, 25, 26, 27, 28, 29, 30,
6326 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6327 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6328
6329static const char kMonthInYear[] = {
6330 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,
6331 0, 0, 0, 0, 0, 0,
6332 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,
6333 1, 1, 1,
6334 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,
6335 2, 2, 2, 2, 2, 2,
6336 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,
6337 3, 3, 3, 3, 3,
6338 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,
6339 4, 4, 4, 4, 4, 4,
6340 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,
6341 5, 5, 5, 5, 5,
6342 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,
6343 6, 6, 6, 6, 6, 6,
6344 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,
6345 7, 7, 7, 7, 7, 7,
6346 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,
6347 8, 8, 8, 8, 8,
6348 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,
6349 9, 9, 9, 9, 9, 9,
6350 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6351 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6352 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6353 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6354
6355 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,
6356 0, 0, 0, 0, 0, 0,
6357 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,
6358 1, 1, 1,
6359 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,
6360 2, 2, 2, 2, 2, 2,
6361 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,
6362 3, 3, 3, 3, 3,
6363 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,
6364 4, 4, 4, 4, 4, 4,
6365 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,
6366 5, 5, 5, 5, 5,
6367 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,
6368 6, 6, 6, 6, 6, 6,
6369 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,
6370 7, 7, 7, 7, 7, 7,
6371 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,
6372 8, 8, 8, 8, 8,
6373 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,
6374 9, 9, 9, 9, 9, 9,
6375 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6376 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6377 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6378 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6379
6380 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,
6381 0, 0, 0, 0, 0, 0,
6382 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,
6383 1, 1, 1, 1,
6384 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,
6385 2, 2, 2, 2, 2, 2,
6386 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,
6387 3, 3, 3, 3, 3,
6388 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,
6389 4, 4, 4, 4, 4, 4,
6390 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,
6391 5, 5, 5, 5, 5,
6392 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,
6393 6, 6, 6, 6, 6, 6,
6394 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,
6395 7, 7, 7, 7, 7, 7,
6396 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,
6397 8, 8, 8, 8, 8,
6398 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,
6399 9, 9, 9, 9, 9, 9,
6400 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6401 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6402 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6403 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6404
6405 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,
6406 0, 0, 0, 0, 0, 0,
6407 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,
6408 1, 1, 1,
6409 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,
6410 2, 2, 2, 2, 2, 2,
6411 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,
6412 3, 3, 3, 3, 3,
6413 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,
6414 4, 4, 4, 4, 4, 4,
6415 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,
6416 5, 5, 5, 5, 5,
6417 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,
6418 6, 6, 6, 6, 6, 6,
6419 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,
6420 7, 7, 7, 7, 7, 7,
6421 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,
6422 8, 8, 8, 8, 8,
6423 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,
6424 9, 9, 9, 9, 9, 9,
6425 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6426 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6427 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6428 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6429
6430
6431// This function works for dates from 1970 to 2099.
6432static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006433 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006434#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006435 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006436#endif
6437
6438 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6439 date %= kDaysIn4Years;
6440
6441 month = kMonthInYear[date];
6442 day = kDayInYear[date];
6443
6444 ASSERT(MakeDay(year, month, day) == save_date);
6445}
6446
6447
6448static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006449 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006450#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006451 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006452#endif
6453
6454 date += kDaysOffset;
6455 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6456 date %= kDaysIn400Years;
6457
6458 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6459
6460 date--;
6461 int yd1 = date / kDaysIn100Years;
6462 date %= kDaysIn100Years;
6463 year += 100 * yd1;
6464
6465 date++;
6466 int yd2 = date / kDaysIn4Years;
6467 date %= kDaysIn4Years;
6468 year += 4 * yd2;
6469
6470 date--;
6471 int yd3 = date / 365;
6472 date %= 365;
6473 year += yd3;
6474
6475 bool is_leap = (!yd1 || yd2) && !yd3;
6476
6477 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006478 ASSERT(is_leap || (date >= 0));
6479 ASSERT((date < 365) || (is_leap && (date < 366)));
6480 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6481 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6482 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006483
6484 if (is_leap) {
6485 day = kDayInYear[2*365 + 1 + date];
6486 month = kMonthInYear[2*365 + 1 + date];
6487 } else {
6488 day = kDayInYear[date];
6489 month = kMonthInYear[date];
6490 }
6491
6492 ASSERT(MakeDay(year, month, day) == save_date);
6493}
6494
6495
6496static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006497 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006498 if (date >= 0 && date < 32 * kDaysIn4Years) {
6499 DateYMDFromTimeAfter1970(date, year, month, day);
6500 } else {
6501 DateYMDFromTimeSlow(date, year, month, day);
6502 }
6503}
6504
6505
6506static Object* Runtime_DateYMDFromTime(Arguments args) {
6507 NoHandleAllocation ha;
6508 ASSERT(args.length() == 2);
6509
6510 CONVERT_DOUBLE_CHECKED(t, args[0]);
6511 CONVERT_CHECKED(JSArray, res_array, args[1]);
6512
6513 int year, month, day;
6514 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6515
6516 res_array->SetElement(0, Smi::FromInt(year));
6517 res_array->SetElement(1, Smi::FromInt(month));
6518 res_array->SetElement(2, Smi::FromInt(day));
6519
6520 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006521}
6522
6523
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006524static Object* Runtime_NewArgumentsFast(Arguments args) {
6525 NoHandleAllocation ha;
6526 ASSERT(args.length() == 3);
6527
6528 JSFunction* callee = JSFunction::cast(args[0]);
6529 Object** parameters = reinterpret_cast<Object**>(args[1]);
6530 const int length = Smi::cast(args[2])->value();
6531
6532 Object* result = Heap::AllocateArgumentsObject(callee, length);
6533 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006534 // Allocate the elements if needed.
6535 if (length > 0) {
6536 // Allocate the fixed array.
6537 Object* obj = Heap::AllocateRawFixedArray(length);
6538 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006539
6540 AssertNoAllocation no_gc;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006541 FixedArray* array = reinterpret_cast<FixedArray*>(obj);
6542 array->set_map(Heap::fixed_array_map());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006543 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006544
6545 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006546 for (int i = 0; i < length; i++) {
6547 array->set(i, *--parameters, mode);
6548 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006549 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006550 }
6551 return result;
6552}
6553
6554
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006555static Object* Runtime_NewClosure(Arguments args) {
6556 HandleScope scope;
6557 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006558 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006559 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006560
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006561 PretenureFlag pretenure = (context->global_context() == *context)
6562 ? TENURED // Allocate global closures in old space.
6563 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006564 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006565 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006566 return *result;
6567}
6568
6569
ager@chromium.org5c838252010-02-19 08:53:10 +00006570static Code* ComputeConstructStub(Handle<JSFunction> function) {
6571 Handle<Object> prototype = Factory::null_value();
6572 if (function->has_instance_prototype()) {
6573 prototype = Handle<Object>(function->instance_prototype());
6574 }
6575 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006576 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006577 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006578 if (code->IsFailure()) {
6579 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6580 }
6581 return Code::cast(code);
6582 }
6583
ager@chromium.org5c838252010-02-19 08:53:10 +00006584 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006585}
6586
6587
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006588static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006589 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006590 ASSERT(args.length() == 1);
6591
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006592 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006593
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006594 // If the constructor isn't a proper function we throw a type error.
6595 if (!constructor->IsJSFunction()) {
6596 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6597 Handle<Object> type_error =
6598 Factory::NewTypeError("not_constructor", arguments);
6599 return Top::Throw(*type_error);
6600 }
6601
6602 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00006603
6604 // If function should not have prototype, construction is not allowed. In this
6605 // case generated code bailouts here, since function has no initial_map.
6606 if (!function->should_have_prototype()) {
6607 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6608 Handle<Object> type_error =
6609 Factory::NewTypeError("not_constructor", arguments);
6610 return Top::Throw(*type_error);
6611 }
6612
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006613#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006614 // Handle stepping into constructors if step into is active.
6615 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006616 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006617 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006618#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006619
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006620 if (function->has_initial_map()) {
6621 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006622 // The 'Function' function ignores the receiver object when
6623 // called using 'new' and creates a new JSFunction object that
6624 // is returned. The receiver object is only used for error
6625 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006626 // JSFunction. Factory::NewJSObject() should not be used to
6627 // allocate JSFunctions since it does not properly initialize
6628 // the shared part of the function. Since the receiver is
6629 // ignored anyway, we use the global object as the receiver
6630 // instead of a new JSFunction object. This way, errors are
6631 // reported the same way whether or not 'Function' is called
6632 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006633 return Top::context()->global();
6634 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006635 }
6636
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006637 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006638 Handle<SharedFunctionInfo> shared(function->shared());
6639 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006640
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006641 bool first_allocation = !function->has_initial_map();
6642 Handle<JSObject> result = Factory::NewJSObject(function);
6643 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006644 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006645 ComputeConstructStub(Handle<JSFunction>(function)));
6646 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006647 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006648
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006649 Counters::constructed_objects.Increment();
6650 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006651
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006652 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006653}
6654
6655
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006656static Object* Runtime_LazyCompile(Arguments args) {
6657 HandleScope scope;
6658 ASSERT(args.length() == 1);
6659
6660 Handle<JSFunction> function = args.at<JSFunction>(0);
6661#ifdef DEBUG
6662 if (FLAG_trace_lazy) {
6663 PrintF("[lazy: ");
6664 function->shared()->name()->Print();
6665 PrintF("]\n");
6666 }
6667#endif
6668
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006669 // Compile the target function. Here we compile using CompileLazyInLoop in
6670 // order to get the optimized version. This helps code like delta-blue
6671 // that calls performance-critical routines through constructors. A
6672 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6673 // direct call. Since the in-loop tracking takes place through CallICs
6674 // this means that things called through constructors are never known to
6675 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006676 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006677 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006678 return Failure::Exception();
6679 }
6680
6681 return function->code();
6682}
6683
6684
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006685static Object* Runtime_GetFunctionDelegate(Arguments args) {
6686 HandleScope scope;
6687 ASSERT(args.length() == 1);
6688 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6689 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6690}
6691
6692
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006693static Object* Runtime_GetConstructorDelegate(Arguments args) {
6694 HandleScope scope;
6695 ASSERT(args.length() == 1);
6696 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6697 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6698}
6699
6700
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006701static Object* Runtime_NewContext(Arguments args) {
6702 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006703 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006704
kasper.lund7276f142008-07-30 08:49:36 +00006705 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006706 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6707 Object* result = Heap::AllocateFunctionContext(length, function);
6708 if (result->IsFailure()) return result;
6709
6710 Top::set_context(Context::cast(result));
6711
kasper.lund7276f142008-07-30 08:49:36 +00006712 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006713}
6714
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006715static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006716 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006717 Object* js_object = object;
6718 if (!js_object->IsJSObject()) {
6719 js_object = js_object->ToObject();
6720 if (js_object->IsFailure()) {
6721 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006722 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006723 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006724 Handle<Object> result =
6725 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6726 return Top::Throw(*result);
6727 }
6728 }
6729
6730 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006731 Heap::AllocateWithContext(Top::context(),
6732 JSObject::cast(js_object),
6733 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006734 if (result->IsFailure()) return result;
6735
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006736 Context* context = Context::cast(result);
6737 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006738
kasper.lund7276f142008-07-30 08:49:36 +00006739 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006740}
6741
6742
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006743static Object* Runtime_PushContext(Arguments args) {
6744 NoHandleAllocation ha;
6745 ASSERT(args.length() == 1);
6746 return PushContextHelper(args[0], false);
6747}
6748
6749
6750static Object* Runtime_PushCatchContext(Arguments args) {
6751 NoHandleAllocation ha;
6752 ASSERT(args.length() == 1);
6753 return PushContextHelper(args[0], true);
6754}
6755
6756
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006757static Object* Runtime_LookupContext(Arguments args) {
6758 HandleScope scope;
6759 ASSERT(args.length() == 2);
6760
6761 CONVERT_ARG_CHECKED(Context, context, 0);
6762 CONVERT_ARG_CHECKED(String, name, 1);
6763
6764 int index;
6765 PropertyAttributes attributes;
6766 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006767 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006768 context->Lookup(name, flags, &index, &attributes);
6769
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006770 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006771 ASSERT(holder->IsJSObject());
6772 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006773 }
6774
6775 // No intermediate context found. Use global object by default.
6776 return Top::context()->global();
6777}
6778
6779
ager@chromium.orga1645e22009-09-09 19:27:10 +00006780// A mechanism to return a pair of Object pointers in registers (if possible).
6781// How this is achieved is calling convention-dependent.
6782// All currently supported x86 compiles uses calling conventions that are cdecl
6783// variants where a 64-bit value is returned in two 32-bit registers
6784// (edx:eax on ia32, r1:r0 on ARM).
6785// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6786// In Win64 calling convention, a struct of two pointers is returned in memory,
6787// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006788#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006789struct ObjectPair {
6790 Object* x;
6791 Object* y;
6792};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006793
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006794static inline ObjectPair MakePair(Object* x, Object* y) {
6795 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006796 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6797 // In Win64 they are assigned to a hidden first argument.
6798 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006799}
6800#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006801typedef uint64_t ObjectPair;
6802static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006803 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006804 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006805}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006806#endif
6807
6808
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006809static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006810 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6811 USE(attributes);
6812 return x->IsTheHole() ? Heap::undefined_value() : x;
6813}
6814
6815
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006816static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6817 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006818 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006819 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006820 JSFunction* context_extension_function =
6821 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006822 // If the holder isn't a context extension object, we just return it
6823 // as the receiver. This allows arguments objects to be used as
6824 // receivers, but only if they are put in the context scope chain
6825 // explicitly via a with-statement.
6826 Object* constructor = holder->map()->constructor();
6827 if (constructor != context_extension_function) return holder;
6828 // Fall back to using the global object as the receiver if the
6829 // property turns out to be a local variable allocated in a context
6830 // extension object - introduced via eval.
6831 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006832}
6833
6834
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006835static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006836 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006837 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006838
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006839 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006840 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006841 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006842 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006843 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006844
6845 int index;
6846 PropertyAttributes attributes;
6847 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006848 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006849 context->Lookup(name, flags, &index, &attributes);
6850
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006851 // If the index is non-negative, the slot has been found in a local
6852 // variable or a parameter. Read it from the context object or the
6853 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006854 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006855 // If the "property" we were looking for is a local variable or an
6856 // argument in a context, the receiver is the global object; see
6857 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6858 JSObject* receiver = Top::context()->global()->global_receiver();
6859 Object* value = (holder->IsContext())
6860 ? Context::cast(*holder)->get(index)
6861 : JSObject::cast(*holder)->GetElement(index);
6862 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006863 }
6864
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006865 // If the holder is found, we read the property from it.
6866 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006867 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006868 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006869 JSObject* receiver;
6870 if (object->IsGlobalObject()) {
6871 receiver = GlobalObject::cast(object)->global_receiver();
6872 } else if (context->is_exception_holder(*holder)) {
6873 receiver = Top::context()->global()->global_receiver();
6874 } else {
6875 receiver = ComputeReceiverForNonGlobal(object);
6876 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006877 // No need to unhole the value here. This is taken care of by the
6878 // GetProperty function.
6879 Object* value = object->GetProperty(*name);
6880 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006881 }
6882
6883 if (throw_error) {
6884 // The property doesn't exist - throw exception.
6885 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006886 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006887 return MakePair(Top::Throw(*reference_error), NULL);
6888 } else {
6889 // The property doesn't exist - return undefined
6890 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6891 }
6892}
6893
6894
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006895static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006896 return LoadContextSlotHelper(args, true);
6897}
6898
6899
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006900static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006901 return LoadContextSlotHelper(args, false);
6902}
6903
6904
6905static Object* Runtime_StoreContextSlot(Arguments args) {
6906 HandleScope scope;
6907 ASSERT(args.length() == 3);
6908
6909 Handle<Object> value(args[0]);
6910 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006911 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912
6913 int index;
6914 PropertyAttributes attributes;
6915 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006916 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006917 context->Lookup(name, flags, &index, &attributes);
6918
6919 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006920 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006921 // Ignore if read_only variable.
6922 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006923 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006924 }
6925 } else {
6926 ASSERT((attributes & READ_ONLY) == 0);
6927 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006928 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006929 USE(result);
6930 ASSERT(!result->IsFailure());
6931 }
6932 return *value;
6933 }
6934
6935 // Slow case: The property is not in a FixedArray context.
6936 // It is either in an JSObject extension context or it was not found.
6937 Handle<JSObject> context_ext;
6938
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006939 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006940 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006941 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006942 } else {
6943 // The property was not found. It needs to be stored in the global context.
6944 ASSERT(attributes == ABSENT);
6945 attributes = NONE;
6946 context_ext = Handle<JSObject>(Top::context()->global());
6947 }
6948
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006949 // Set the property, but ignore if read_only variable on the context
6950 // extension object itself.
6951 if ((attributes & READ_ONLY) == 0 ||
6952 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006953 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6954 if (set.is_null()) {
6955 // Failure::Exception is converted to a null handle in the
6956 // handle-based methods such as SetProperty. We therefore need
6957 // to convert null handles back to exceptions.
6958 ASSERT(Top::has_pending_exception());
6959 return Failure::Exception();
6960 }
6961 }
6962 return *value;
6963}
6964
6965
6966static Object* Runtime_Throw(Arguments args) {
6967 HandleScope scope;
6968 ASSERT(args.length() == 1);
6969
6970 return Top::Throw(args[0]);
6971}
6972
6973
6974static Object* Runtime_ReThrow(Arguments args) {
6975 HandleScope scope;
6976 ASSERT(args.length() == 1);
6977
6978 return Top::ReThrow(args[0]);
6979}
6980
6981
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006982static Object* Runtime_PromoteScheduledException(Arguments args) {
6983 ASSERT_EQ(0, args.length());
6984 return Top::PromoteScheduledException();
6985}
6986
6987
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006988static Object* Runtime_ThrowReferenceError(Arguments args) {
6989 HandleScope scope;
6990 ASSERT(args.length() == 1);
6991
6992 Handle<Object> name(args[0]);
6993 Handle<Object> reference_error =
6994 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6995 return Top::Throw(*reference_error);
6996}
6997
6998
6999static Object* Runtime_StackOverflow(Arguments args) {
7000 NoHandleAllocation na;
7001 return Top::StackOverflow();
7002}
7003
7004
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007005static Object* Runtime_StackGuard(Arguments args) {
7006 ASSERT(args.length() == 1);
7007
7008 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007009 if (StackGuard::IsStackOverflow()) {
7010 return Runtime_StackOverflow(args);
7011 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007012
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007013 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007014}
7015
7016
7017// NOTE: These PrintXXX functions are defined for all builds (not just
7018// DEBUG builds) because we may want to be able to trace function
7019// calls in all modes.
7020static void PrintString(String* str) {
7021 // not uncommon to have empty strings
7022 if (str->length() > 0) {
7023 SmartPointer<char> s =
7024 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
7025 PrintF("%s", *s);
7026 }
7027}
7028
7029
7030static void PrintObject(Object* obj) {
7031 if (obj->IsSmi()) {
7032 PrintF("%d", Smi::cast(obj)->value());
7033 } else if (obj->IsString() || obj->IsSymbol()) {
7034 PrintString(String::cast(obj));
7035 } else if (obj->IsNumber()) {
7036 PrintF("%g", obj->Number());
7037 } else if (obj->IsFailure()) {
7038 PrintF("<failure>");
7039 } else if (obj->IsUndefined()) {
7040 PrintF("<undefined>");
7041 } else if (obj->IsNull()) {
7042 PrintF("<null>");
7043 } else if (obj->IsTrue()) {
7044 PrintF("<true>");
7045 } else if (obj->IsFalse()) {
7046 PrintF("<false>");
7047 } else {
7048 PrintF("%p", obj);
7049 }
7050}
7051
7052
7053static int StackSize() {
7054 int n = 0;
7055 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
7056 return n;
7057}
7058
7059
7060static void PrintTransition(Object* result) {
7061 // indentation
7062 { const int nmax = 80;
7063 int n = StackSize();
7064 if (n <= nmax)
7065 PrintF("%4d:%*s", n, n, "");
7066 else
7067 PrintF("%4d:%*s", n, nmax, "...");
7068 }
7069
7070 if (result == NULL) {
7071 // constructor calls
7072 JavaScriptFrameIterator it;
7073 JavaScriptFrame* frame = it.frame();
7074 if (frame->IsConstructor()) PrintF("new ");
7075 // function name
7076 Object* fun = frame->function();
7077 if (fun->IsJSFunction()) {
7078 PrintObject(JSFunction::cast(fun)->shared()->name());
7079 } else {
7080 PrintObject(fun);
7081 }
7082 // function arguments
7083 // (we are intentionally only printing the actually
7084 // supplied parameters, not all parameters required)
7085 PrintF("(this=");
7086 PrintObject(frame->receiver());
7087 const int length = frame->GetProvidedParametersCount();
7088 for (int i = 0; i < length; i++) {
7089 PrintF(", ");
7090 PrintObject(frame->GetParameter(i));
7091 }
7092 PrintF(") {\n");
7093
7094 } else {
7095 // function result
7096 PrintF("} -> ");
7097 PrintObject(result);
7098 PrintF("\n");
7099 }
7100}
7101
7102
7103static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007104 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007105 NoHandleAllocation ha;
7106 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007107 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108}
7109
7110
7111static Object* Runtime_TraceExit(Arguments args) {
7112 NoHandleAllocation ha;
7113 PrintTransition(args[0]);
7114 return args[0]; // return TOS
7115}
7116
7117
7118static Object* Runtime_DebugPrint(Arguments args) {
7119 NoHandleAllocation ha;
7120 ASSERT(args.length() == 1);
7121
7122#ifdef DEBUG
7123 if (args[0]->IsString()) {
7124 // If we have a string, assume it's a code "marker"
7125 // and print some interesting cpu debugging info.
7126 JavaScriptFrameIterator it;
7127 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007128 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7129 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007130 } else {
7131 PrintF("DebugPrint: ");
7132 }
7133 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007134 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007135 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007136 HeapObject::cast(args[0])->map()->Print();
7137 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007138#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007139 // ShortPrint is available in release mode. Print is not.
7140 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141#endif
7142 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007143 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007144
7145 return args[0]; // return TOS
7146}
7147
7148
7149static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007150 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007151 NoHandleAllocation ha;
7152 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007153 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007154}
7155
7156
mads.s.ager31e71382008-08-13 09:32:07 +00007157static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007158 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007159 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007160
7161 // According to ECMA-262, section 15.9.1, page 117, the precision of
7162 // the number in a Date object representing a particular instant in
7163 // time is milliseconds. Therefore, we floor the result of getting
7164 // the OS time.
7165 double millis = floor(OS::TimeCurrentMillis());
7166 return Heap::NumberFromDouble(millis);
7167}
7168
7169
7170static Object* Runtime_DateParseString(Arguments args) {
7171 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007172 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007173
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007174 CONVERT_ARG_CHECKED(String, str, 0);
7175 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007176
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007177 CONVERT_ARG_CHECKED(JSArray, output, 1);
7178 RUNTIME_ASSERT(output->HasFastElements());
7179
7180 AssertNoAllocation no_allocation;
7181
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007182 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007183 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7184 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007185 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007186 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007187 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007188 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007189 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7190 }
7191
7192 if (result) {
7193 return *output;
7194 } else {
7195 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007196 }
7197}
7198
7199
7200static Object* Runtime_DateLocalTimezone(Arguments args) {
7201 NoHandleAllocation ha;
7202 ASSERT(args.length() == 1);
7203
7204 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007205 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007206 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7207}
7208
7209
7210static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7211 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007212 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007213
7214 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7215}
7216
7217
7218static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7219 NoHandleAllocation ha;
7220 ASSERT(args.length() == 1);
7221
7222 CONVERT_DOUBLE_CHECKED(x, args[0]);
7223 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7224}
7225
7226
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007227static Object* Runtime_GlobalReceiver(Arguments args) {
7228 ASSERT(args.length() == 1);
7229 Object* global = args[0];
7230 if (!global->IsJSGlobalObject()) return Heap::null_value();
7231 return JSGlobalObject::cast(global)->global_receiver();
7232}
7233
7234
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007235static Object* Runtime_CompileString(Arguments args) {
7236 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007237 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007238 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007239 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007240
ager@chromium.org381abbb2009-02-25 13:23:22 +00007241 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007242 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007243 Compiler::ValidationState validate = (is_json->IsTrue())
7244 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007245 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7246 context,
7247 true,
7248 validate);
7249 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007250 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007251 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007252 return *fun;
7253}
7254
7255
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007256static ObjectPair CompileGlobalEval(Handle<String> source,
7257 Handle<Object> receiver) {
7258 // Deal with a normal eval call with a string argument. Compile it
7259 // and return the compiled function bound in the local context.
7260 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
7261 source,
7262 Handle<Context>(Top::context()),
7263 Top::context()->IsGlobalContext(),
7264 Compiler::DONT_VALIDATE_JSON);
7265 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7266 Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
7267 shared,
7268 Handle<Context>(Top::context()),
7269 NOT_TENURED);
7270 return MakePair(*compiled, *receiver);
7271}
7272
7273
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007274static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7275 ASSERT(args.length() == 3);
7276 if (!args[0]->IsJSFunction()) {
7277 return MakePair(Top::ThrowIllegalOperation(), NULL);
7278 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007279
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007280 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007281 Handle<JSFunction> callee = args.at<JSFunction>(0);
7282 Handle<Object> receiver; // Will be overwritten.
7283
7284 // Compute the calling context.
7285 Handle<Context> context = Handle<Context>(Top::context());
7286#ifdef DEBUG
7287 // Make sure Top::context() agrees with the old code that traversed
7288 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007289 StackFrameLocator locator;
7290 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007291 ASSERT(Context::cast(frame->context()) == *context);
7292#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007293
7294 // Find where the 'eval' symbol is bound. It is unaliased only if
7295 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007296 int index = -1;
7297 PropertyAttributes attributes = ABSENT;
7298 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007299 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7300 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007301 // Stop search when eval is found or when the global context is
7302 // reached.
7303 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007304 if (context->is_function_context()) {
7305 context = Handle<Context>(Context::cast(context->closure()->context()));
7306 } else {
7307 context = Handle<Context>(context->previous());
7308 }
7309 }
7310
iposva@chromium.org245aa852009-02-10 00:49:54 +00007311 // If eval could not be resolved, it has been deleted and we need to
7312 // throw a reference error.
7313 if (attributes == ABSENT) {
7314 Handle<Object> name = Factory::eval_symbol();
7315 Handle<Object> reference_error =
7316 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007317 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007318 }
7319
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007320 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007321 // 'eval' is not bound in the global context. Just call the function
7322 // with the given arguments. This is not necessarily the global eval.
7323 if (receiver->IsContext()) {
7324 context = Handle<Context>::cast(receiver);
7325 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007326 } else if (receiver->IsJSContextExtensionObject()) {
7327 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007328 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007329 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007330 }
7331
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007332 // 'eval' is bound in the global context, but it may have been overwritten.
7333 // Compare it to the builtin 'GlobalEval' function to make sure.
7334 if (*callee != Top::global_context()->global_eval_fun() ||
7335 !args[1]->IsString()) {
7336 return MakePair(*callee, Top::context()->global()->global_receiver());
7337 }
7338
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00007339 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
7340}
7341
7342
7343static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
7344 ASSERT(args.length() == 3);
7345 if (!args[0]->IsJSFunction()) {
7346 return MakePair(Top::ThrowIllegalOperation(), NULL);
7347 }
7348
7349 HandleScope scope;
7350 Handle<JSFunction> callee = args.at<JSFunction>(0);
7351
7352 // 'eval' is bound in the global context, but it may have been overwritten.
7353 // Compare it to the builtin 'GlobalEval' function to make sure.
7354 if (*callee != Top::global_context()->global_eval_fun() ||
7355 !args[1]->IsString()) {
7356 return MakePair(*callee, Top::context()->global()->global_receiver());
7357 }
7358
7359 return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007360}
7361
7362
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007363static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7364 // This utility adjusts the property attributes for newly created Function
7365 // object ("new Function(...)") by changing the map.
7366 // All it does is changing the prototype property to enumerable
7367 // as specified in ECMA262, 15.3.5.2.
7368 HandleScope scope;
7369 ASSERT(args.length() == 1);
7370 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7371 ASSERT(func->map()->instance_type() ==
7372 Top::function_instance_map()->instance_type());
7373 ASSERT(func->map()->instance_size() ==
7374 Top::function_instance_map()->instance_size());
7375 func->set_map(*Top::function_instance_map());
7376 return *func;
7377}
7378
7379
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007380// Push an array unto an array of arrays if it is not already in the
7381// array. Returns true if the element was pushed on the stack and
7382// false otherwise.
7383static Object* Runtime_PushIfAbsent(Arguments args) {
7384 ASSERT(args.length() == 2);
7385 CONVERT_CHECKED(JSArray, array, args[0]);
7386 CONVERT_CHECKED(JSArray, element, args[1]);
7387 RUNTIME_ASSERT(array->HasFastElements());
7388 int length = Smi::cast(array->length())->value();
7389 FixedArray* elements = FixedArray::cast(array->elements());
7390 for (int i = 0; i < length; i++) {
7391 if (elements->get(i) == element) return Heap::false_value();
7392 }
7393 Object* obj = array->SetFastElement(length, element);
7394 if (obj->IsFailure()) return obj;
7395 return Heap::true_value();
7396}
7397
7398
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007399/**
7400 * A simple visitor visits every element of Array's.
7401 * The backend storage can be a fixed array for fast elements case,
7402 * or a dictionary for sparse array. Since Dictionary is a subtype
7403 * of FixedArray, the class can be used by both fast and slow cases.
7404 * The second parameter of the constructor, fast_elements, specifies
7405 * whether the storage is a FixedArray or Dictionary.
7406 *
7407 * An index limit is used to deal with the situation that a result array
7408 * length overflows 32-bit non-negative integer.
7409 */
7410class ArrayConcatVisitor {
7411 public:
7412 ArrayConcatVisitor(Handle<FixedArray> storage,
7413 uint32_t index_limit,
7414 bool fast_elements) :
7415 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007416 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007417
7418 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007419 if (i >= index_limit_ - index_offset_) return;
7420 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007421
7422 if (fast_elements_) {
7423 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7424 storage_->set(index, *elm);
7425
7426 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007427 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7428 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007429 Factory::DictionaryAtNumberPut(dict, index, elm);
7430 if (!result.is_identical_to(dict))
7431 storage_ = result;
7432 }
7433 }
7434
7435 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007436 if (index_limit_ - index_offset_ < delta) {
7437 index_offset_ = index_limit_;
7438 } else {
7439 index_offset_ += delta;
7440 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007441 }
7442
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007443 Handle<FixedArray> storage() { return storage_; }
7444
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007445 private:
7446 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007447 // Limit on the accepted indices. Elements with indices larger than the
7448 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007449 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007450 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007451 uint32_t index_offset_;
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007452 const bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007453};
7454
7455
ager@chromium.org3811b432009-10-28 14:53:37 +00007456template<class ExternalArrayClass, class ElementType>
7457static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7458 bool elements_are_ints,
7459 bool elements_are_guaranteed_smis,
7460 uint32_t range,
7461 ArrayConcatVisitor* visitor) {
7462 Handle<ExternalArrayClass> array(
7463 ExternalArrayClass::cast(receiver->elements()));
7464 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7465
7466 if (visitor != NULL) {
7467 if (elements_are_ints) {
7468 if (elements_are_guaranteed_smis) {
7469 for (uint32_t j = 0; j < len; j++) {
7470 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7471 visitor->visit(j, e);
7472 }
7473 } else {
7474 for (uint32_t j = 0; j < len; j++) {
7475 int64_t val = static_cast<int64_t>(array->get(j));
7476 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7477 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7478 visitor->visit(j, e);
7479 } else {
7480 Handle<Object> e(
7481 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7482 visitor->visit(j, e);
7483 }
7484 }
7485 }
7486 } else {
7487 for (uint32_t j = 0; j < len; j++) {
7488 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7489 visitor->visit(j, e);
7490 }
7491 }
7492 }
7493
7494 return len;
7495}
7496
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007497/**
7498 * A helper function that visits elements of a JSObject. Only elements
7499 * whose index between 0 and range (exclusive) are visited.
7500 *
7501 * If the third parameter, visitor, is not NULL, the visitor is called
7502 * with parameters, 'visitor_index_offset + element index' and the element.
7503 *
7504 * It returns the number of visisted elements.
7505 */
7506static uint32_t IterateElements(Handle<JSObject> receiver,
7507 uint32_t range,
7508 ArrayConcatVisitor* visitor) {
7509 uint32_t num_of_elements = 0;
7510
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007511 switch (receiver->GetElementsKind()) {
7512 case JSObject::FAST_ELEMENTS: {
7513 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7514 uint32_t len = elements->length();
7515 if (range < len) {
7516 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007517 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007518
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007519 for (uint32_t j = 0; j < len; j++) {
7520 Handle<Object> e(elements->get(j));
7521 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007522 num_of_elements++;
7523 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007524 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007525 }
7526 }
7527 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007528 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007529 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007530 case JSObject::PIXEL_ELEMENTS: {
7531 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7532 uint32_t len = pixels->length();
7533 if (range < len) {
7534 len = range;
7535 }
7536
7537 for (uint32_t j = 0; j < len; j++) {
7538 num_of_elements++;
7539 if (visitor != NULL) {
7540 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7541 visitor->visit(j, e);
7542 }
7543 }
7544 break;
7545 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007546 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7547 num_of_elements =
7548 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7549 receiver, true, true, range, visitor);
7550 break;
7551 }
7552 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7553 num_of_elements =
7554 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7555 receiver, true, true, range, visitor);
7556 break;
7557 }
7558 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7559 num_of_elements =
7560 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7561 receiver, true, true, range, visitor);
7562 break;
7563 }
7564 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7565 num_of_elements =
7566 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7567 receiver, true, true, range, visitor);
7568 break;
7569 }
7570 case JSObject::EXTERNAL_INT_ELEMENTS: {
7571 num_of_elements =
7572 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7573 receiver, true, false, range, visitor);
7574 break;
7575 }
7576 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7577 num_of_elements =
7578 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7579 receiver, true, false, range, visitor);
7580 break;
7581 }
7582 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7583 num_of_elements =
7584 IterateExternalArrayElements<ExternalFloatArray, float>(
7585 receiver, false, false, range, visitor);
7586 break;
7587 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007588 case JSObject::DICTIONARY_ELEMENTS: {
7589 Handle<NumberDictionary> dict(receiver->element_dictionary());
7590 uint32_t capacity = dict->Capacity();
7591 for (uint32_t j = 0; j < capacity; j++) {
7592 Handle<Object> k(dict->KeyAt(j));
7593 if (dict->IsKey(*k)) {
7594 ASSERT(k->IsNumber());
7595 uint32_t index = static_cast<uint32_t>(k->Number());
7596 if (index < range) {
7597 num_of_elements++;
7598 if (visitor) {
7599 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7600 }
7601 }
7602 }
7603 }
7604 break;
7605 }
7606 default:
7607 UNREACHABLE();
7608 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007609 }
7610
7611 return num_of_elements;
7612}
7613
7614
7615/**
7616 * A helper function that visits elements of an Array object, and elements
7617 * on its prototypes.
7618 *
7619 * Elements on prototypes are visited first, and only elements whose indices
7620 * less than Array length are visited.
7621 *
7622 * If a ArrayConcatVisitor object is given, the visitor is called with
7623 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007624 *
7625 * The returned number of elements is an upper bound on the actual number
7626 * of elements added. If the same element occurs in more than one object
7627 * in the array's prototype chain, it will be counted more than once, but
7628 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007629 */
7630static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7631 ArrayConcatVisitor* visitor) {
7632 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7633 Handle<Object> obj = array;
7634
7635 static const int kEstimatedPrototypes = 3;
7636 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7637
7638 // Visit prototype first. If an element on the prototype is shadowed by
7639 // the inheritor using the same index, the ArrayConcatVisitor visits
7640 // the prototype element before the shadowing element.
7641 // The visitor can simply overwrite the old value by new value using
7642 // the same index. This follows Array::concat semantics.
7643 while (!obj->IsNull()) {
7644 objects.Add(Handle<JSObject>::cast(obj));
7645 obj = Handle<Object>(obj->GetPrototype());
7646 }
7647
7648 uint32_t nof_elements = 0;
7649 for (int i = objects.length() - 1; i >= 0; i--) {
7650 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007651 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007652 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007653
7654 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7655 nof_elements = JSObject::kMaxElementCount;
7656 } else {
7657 nof_elements += encountered_elements;
7658 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007659 }
7660
7661 return nof_elements;
7662}
7663
7664
7665/**
7666 * A helper function of Runtime_ArrayConcat.
7667 *
7668 * The first argument is an Array of arrays and objects. It is the
7669 * same as the arguments array of Array::concat JS function.
7670 *
7671 * If an argument is an Array object, the function visits array
7672 * elements. If an argument is not an Array object, the function
7673 * visits the object as if it is an one-element array.
7674 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007675 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007676 * non-negative number is used as new length. For example, if one
7677 * array length is 2^32 - 1, second array length is 1, the
7678 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007679 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7680 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007681 */
7682static uint32_t IterateArguments(Handle<JSArray> arguments,
7683 ArrayConcatVisitor* visitor) {
7684 uint32_t visited_elements = 0;
7685 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7686
7687 for (uint32_t i = 0; i < num_of_args; i++) {
7688 Handle<Object> obj(arguments->GetElement(i));
7689 if (obj->IsJSArray()) {
7690 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7691 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7692 uint32_t nof_elements =
7693 IterateArrayAndPrototypeElements(array, visitor);
7694 // Total elements of array and its prototype chain can be more than
7695 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007696 // the array length number of elements. We use the length as an estimate
7697 // for the actual number of elements added.
7698 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7699 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7700 visited_elements = JSArray::kMaxElementCount;
7701 } else {
7702 visited_elements += added_elements;
7703 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007704 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007705 } else {
7706 if (visitor) {
7707 visitor->visit(0, obj);
7708 visitor->increase_index_offset(1);
7709 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007710 if (visited_elements < JSArray::kMaxElementCount) {
7711 visited_elements++;
7712 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007713 }
7714 }
7715 return visited_elements;
7716}
7717
7718
7719/**
7720 * Array::concat implementation.
7721 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007722 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7723 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007724 */
7725static Object* Runtime_ArrayConcat(Arguments args) {
7726 ASSERT(args.length() == 1);
7727 HandleScope handle_scope;
7728
7729 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7730 Handle<JSArray> arguments(arg_arrays);
7731
7732 // Pass 1: estimate the number of elements of the result
7733 // (it could be more than real numbers if prototype has elements).
7734 uint32_t result_length = 0;
7735 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7736
7737 { AssertNoAllocation nogc;
7738 for (uint32_t i = 0; i < num_of_args; i++) {
7739 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007740 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007741 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007742 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007743 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7744 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007745 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007746 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007747 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7748 result_length = JSObject::kMaxElementCount;
7749 break;
7750 }
7751 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007752 }
7753 }
7754
7755 // Allocate an empty array, will set length and content later.
7756 Handle<JSArray> result = Factory::NewJSArray(0);
7757
7758 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7759 // If estimated number of elements is more than half of length, a
7760 // fixed array (fast case) is more time and space-efficient than a
7761 // dictionary.
7762 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7763
7764 Handle<FixedArray> storage;
7765 if (fast_case) {
7766 // The backing storage array must have non-existing elements to
7767 // preserve holes across concat operations.
7768 storage = Factory::NewFixedArrayWithHoles(result_length);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007769 result->set_map(*Factory::GetFastElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007770 } else {
7771 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7772 uint32_t at_least_space_for = estimate_nof_elements +
7773 (estimate_nof_elements >> 2);
7774 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007775 Factory::NewNumberDictionary(at_least_space_for));
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007776 result->set_map(*Factory::GetSlowElementsMap(Handle<Map>(result->map())));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007777 }
7778
7779 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7780
7781 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7782
7783 IterateArguments(arguments, &visitor);
7784
7785 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007786 // Please note the storage might have changed in the visitor.
7787 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007788
7789 return *result;
7790}
7791
7792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007793// This will not allocate (flatten the string), but it may run
7794// very slowly for very deeply nested ConsStrings. For debugging use only.
7795static Object* Runtime_GlobalPrint(Arguments args) {
7796 NoHandleAllocation ha;
7797 ASSERT(args.length() == 1);
7798
7799 CONVERT_CHECKED(String, string, args[0]);
7800 StringInputBuffer buffer(string);
7801 while (buffer.has_more()) {
7802 uint16_t character = buffer.GetNext();
7803 PrintF("%c", character);
7804 }
7805 return string;
7806}
7807
ager@chromium.org5ec48922009-05-05 07:25:34 +00007808// Moves all own elements of an object, that are below a limit, to positions
7809// starting at zero. All undefined values are placed after non-undefined values,
7810// and are followed by non-existing element. Does not change the length
7811// property.
7812// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007813static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007814 ASSERT(args.length() == 2);
7815 CONVERT_CHECKED(JSObject, object, args[0]);
7816 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7817 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007818}
7819
7820
7821// Move contents of argument 0 (an array) to argument 1 (an array)
7822static Object* Runtime_MoveArrayContents(Arguments args) {
7823 ASSERT(args.length() == 2);
7824 CONVERT_CHECKED(JSArray, from, args[0]);
7825 CONVERT_CHECKED(JSArray, to, args[1]);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007826 HeapObject* new_elements = from->elements();
7827 Object* new_map;
7828 if (new_elements->map() == Heap::fixed_array_map()) {
7829 new_map = to->map()->GetFastElementsMap();
7830 } else {
7831 new_map = to->map()->GetSlowElementsMap();
7832 }
7833 if (new_map->IsFailure()) return new_map;
7834 to->set_map(Map::cast(new_map));
7835 to->set_elements(new_elements);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007836 to->set_length(from->length());
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00007837 Object* obj = from->ResetElements();
7838 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007839 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007840 return to;
7841}
7842
7843
7844// How many elements does this array have?
7845static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7846 ASSERT(args.length() == 1);
7847 CONVERT_CHECKED(JSArray, array, args[0]);
7848 HeapObject* elements = array->elements();
7849 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007850 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007851 } else {
7852 return array->length();
7853 }
7854}
7855
7856
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007857static Object* Runtime_SwapElements(Arguments args) {
7858 HandleScope handle_scope;
7859
7860 ASSERT_EQ(3, args.length());
7861
ager@chromium.orgac091b72010-05-05 07:34:42 +00007862 CONVERT_ARG_CHECKED(JSObject, object, 0);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007863 Handle<Object> key1 = args.at<Object>(1);
7864 Handle<Object> key2 = args.at<Object>(2);
7865
7866 uint32_t index1, index2;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007867 if (!key1->ToArrayIndex(&index1)
7868 || !key2->ToArrayIndex(&index2)) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00007869 return Top::ThrowIllegalOperation();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007870 }
7871
ager@chromium.orgac091b72010-05-05 07:34:42 +00007872 Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
7873 Handle<Object> tmp1 = GetElement(jsobject, index1);
7874 Handle<Object> tmp2 = GetElement(jsobject, index2);
7875
7876 SetElement(jsobject, index1, tmp2);
7877 SetElement(jsobject, index2, tmp1);
7878
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00007879 return Heap::undefined_value();
7880}
7881
7882
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007883// Returns an array that tells you where in the [0, length) interval an array
7884// might have elements. Can either return keys or intervals. Keys can have
7885// gaps in (undefined). Intervals can also span over some undefined keys.
7886static Object* Runtime_GetArrayKeys(Arguments args) {
7887 ASSERT(args.length() == 2);
7888 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007889 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007890 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007891 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007892 // Create an array and get all the keys into it, then remove all the
7893 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007894 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007895 int keys_length = keys->length();
7896 for (int i = 0; i < keys_length; i++) {
7897 Object* key = keys->get(i);
7898 uint32_t index;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007899 if (!key->ToArrayIndex(&index) || index >= length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007900 // Zap invalid keys.
7901 keys->set_undefined(i);
7902 }
7903 }
7904 return *Factory::NewJSArrayWithElements(keys);
7905 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007906 ASSERT(array->HasFastElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007907 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7908 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007909 single_interval->set(0, Smi::FromInt(-1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00007910 uint32_t actual_length =
7911 static_cast<uint32_t>(FixedArray::cast(array->elements())->length());
ager@chromium.org5ec48922009-05-05 07:25:34 +00007912 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007913 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007914 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007915 single_interval->set(1, *length_object);
7916 return *Factory::NewJSArrayWithElements(single_interval);
7917 }
7918}
7919
7920
7921// DefineAccessor takes an optional final argument which is the
7922// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7923// to the way accessors are implemented, it is set for both the getter
7924// and setter on the first call to DefineAccessor and ignored on
7925// subsequent calls.
7926static Object* Runtime_DefineAccessor(Arguments args) {
7927 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7928 // Compute attributes.
7929 PropertyAttributes attributes = NONE;
7930 if (args.length() == 5) {
7931 CONVERT_CHECKED(Smi, attrs, args[4]);
7932 int value = attrs->value();
7933 // Only attribute bits should be set.
7934 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7935 attributes = static_cast<PropertyAttributes>(value);
7936 }
7937
7938 CONVERT_CHECKED(JSObject, obj, args[0]);
7939 CONVERT_CHECKED(String, name, args[1]);
7940 CONVERT_CHECKED(Smi, flag, args[2]);
7941 CONVERT_CHECKED(JSFunction, fun, args[3]);
7942 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7943}
7944
7945
7946static Object* Runtime_LookupAccessor(Arguments args) {
7947 ASSERT(args.length() == 3);
7948 CONVERT_CHECKED(JSObject, obj, args[0]);
7949 CONVERT_CHECKED(String, name, args[1]);
7950 CONVERT_CHECKED(Smi, flag, args[2]);
7951 return obj->LookupAccessor(name, flag->value() == 0);
7952}
7953
7954
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007955#ifdef ENABLE_DEBUGGER_SUPPORT
7956static Object* Runtime_DebugBreak(Arguments args) {
7957 ASSERT(args.length() == 0);
7958 return Execution::DebugBreakHelper();
7959}
7960
7961
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007962// Helper functions for wrapping and unwrapping stack frame ids.
7963static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007964 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007965 return Smi::FromInt(id >> 2);
7966}
7967
7968
7969static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7970 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7971}
7972
7973
7974// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007975// args[0]: debug event listener function to set or null or undefined for
7976// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007977// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007978static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007979 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007980 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7981 args[0]->IsUndefined() ||
7982 args[0]->IsNull());
7983 Handle<Object> callback = args.at<Object>(0);
7984 Handle<Object> data = args.at<Object>(1);
7985 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007986
7987 return Heap::undefined_value();
7988}
7989
7990
7991static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007992 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007993 StackGuard::DebugBreak();
7994 return Heap::undefined_value();
7995}
7996
7997
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007998static Object* DebugLookupResultValue(Object* receiver, String* name,
7999 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00008000 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008001 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008002 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008003 case NORMAL:
8004 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008005 if (value->IsTheHole()) {
8006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008007 }
8008 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008009 case FIELD:
8010 value =
8011 JSObject::cast(
8012 result->holder())->FastPropertyAt(result->GetFieldIndex());
8013 if (value->IsTheHole()) {
8014 return Heap::undefined_value();
8015 }
8016 return value;
8017 case CONSTANT_FUNCTION:
8018 return result->GetConstantFunction();
8019 case CALLBACKS: {
8020 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008021 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008022 value = receiver->GetPropertyWithCallback(
8023 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00008024 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00008025 value = Top::pending_exception();
8026 Top::clear_pending_exception();
8027 if (caught_exception != NULL) {
8028 *caught_exception = true;
8029 }
8030 }
8031 return value;
8032 } else {
8033 return Heap::undefined_value();
8034 }
8035 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008036 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008037 case MAP_TRANSITION:
8038 case CONSTANT_TRANSITION:
8039 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008040 return Heap::undefined_value();
8041 default:
8042 UNREACHABLE();
8043 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008044 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008045 return Heap::undefined_value();
8046}
8047
8048
ager@chromium.org32912102009-01-16 10:38:43 +00008049// Get debugger related details for an object property.
8050// args[0]: object holding property
8051// args[1]: name of the property
8052//
8053// The array returned contains the following information:
8054// 0: Property value
8055// 1: Property details
8056// 2: Property value is exception
8057// 3: Getter function if defined
8058// 4: Setter function if defined
8059// Items 2-4 are only filled if the property has either a getter or a setter
8060// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008061static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008062 HandleScope scope;
8063
8064 ASSERT(args.length() == 2);
8065
8066 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8067 CONVERT_ARG_CHECKED(String, name, 1);
8068
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00008069 // Make sure to set the current context to the context before the debugger was
8070 // entered (if the debugger is entered). The reason for switching context here
8071 // is that for some property lookups (accessors and interceptors) callbacks
8072 // into the embedding application can occour, and the embedding application
8073 // could have the assumption that its own global context is the current
8074 // context and not some internal debugger context.
8075 SaveContext save;
8076 if (Debug::InDebugger()) {
8077 Top::set_context(*Debug::debugger_entry()->GetContext());
8078 }
8079
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008080 // Skip the global proxy as it has no properties and always delegates to the
8081 // real global object.
8082 if (obj->IsJSGlobalProxy()) {
8083 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
8084 }
8085
8086
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008087 // Check if the name is trivially convertible to an index and get the element
8088 // if so.
8089 uint32_t index;
8090 if (name->AsArrayIndex(&index)) {
8091 Handle<FixedArray> details = Factory::NewFixedArray(2);
8092 details->set(0, Runtime::GetElementOrCharAt(obj, index));
8093 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
8094 return *Factory::NewJSArrayWithElements(details);
8095 }
8096
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008097 // Find the number of objects making up this.
8098 int length = LocalPrototypeChainLength(*obj);
8099
8100 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008101 Handle<JSObject> jsproto = obj;
8102 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008103 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008104 jsproto->LocalLookup(*name, &result);
8105 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008106 // LookupResult is not GC safe as it holds raw object pointers.
8107 // GC can happen later in this code so put the required fields into
8108 // local variables using handles when required for later use.
8109 PropertyType result_type = result.type();
8110 Handle<Object> result_callback_obj;
8111 if (result_type == CALLBACKS) {
8112 result_callback_obj = Handle<Object>(result.GetCallbackObject());
8113 }
8114 Smi* property_details = result.GetPropertyDetails().AsSmi();
8115 // DebugLookupResultValue can cause GC so details from LookupResult needs
8116 // to be copied to handles before this.
8117 bool caught_exception = false;
8118 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
8119 &caught_exception);
8120 if (raw_value->IsFailure()) return raw_value;
8121 Handle<Object> value(raw_value);
8122
8123 // If the callback object is a fixed array then it contains JavaScript
8124 // getter and/or setter.
8125 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
8126 result_callback_obj->IsFixedArray();
8127 Handle<FixedArray> details =
8128 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
8129 details->set(0, *value);
8130 details->set(1, property_details);
8131 if (hasJavaScriptAccessors) {
8132 details->set(2,
8133 caught_exception ? Heap::true_value()
8134 : Heap::false_value());
8135 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8136 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8137 }
8138
8139 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008140 }
8141 if (i < length - 1) {
8142 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8143 }
8144 }
8145
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008146 return Heap::undefined_value();
8147}
8148
8149
8150static Object* Runtime_DebugGetProperty(Arguments args) {
8151 HandleScope scope;
8152
8153 ASSERT(args.length() == 2);
8154
8155 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8156 CONVERT_ARG_CHECKED(String, name, 1);
8157
8158 LookupResult result;
8159 obj->Lookup(*name, &result);
8160 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008161 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008162 }
8163 return Heap::undefined_value();
8164}
8165
8166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008167// Return the property type calculated from the property details.
8168// args[0]: smi with property details.
8169static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8170 ASSERT(args.length() == 1);
8171 CONVERT_CHECKED(Smi, details, args[0]);
8172 PropertyType type = PropertyDetails(details).type();
8173 return Smi::FromInt(static_cast<int>(type));
8174}
8175
8176
8177// Return the property attribute calculated from the property details.
8178// args[0]: smi with property details.
8179static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8180 ASSERT(args.length() == 1);
8181 CONVERT_CHECKED(Smi, details, args[0]);
8182 PropertyAttributes attributes = PropertyDetails(details).attributes();
8183 return Smi::FromInt(static_cast<int>(attributes));
8184}
8185
8186
8187// Return the property insertion index calculated from the property details.
8188// args[0]: smi with property details.
8189static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8190 ASSERT(args.length() == 1);
8191 CONVERT_CHECKED(Smi, details, args[0]);
8192 int index = PropertyDetails(details).index();
8193 return Smi::FromInt(index);
8194}
8195
8196
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008197// Return property value from named interceptor.
8198// args[0]: object
8199// args[1]: property name
8200static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8201 HandleScope scope;
8202 ASSERT(args.length() == 2);
8203 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8204 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8205 CONVERT_ARG_CHECKED(String, name, 1);
8206
8207 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008208 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008209}
8210
8211
8212// Return element value from indexed interceptor.
8213// args[0]: object
8214// args[1]: index
8215static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8216 HandleScope scope;
8217 ASSERT(args.length() == 2);
8218 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8219 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8220 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8221
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008222 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008223}
8224
8225
8226static Object* Runtime_CheckExecutionState(Arguments args) {
8227 ASSERT(args.length() >= 1);
8228 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008229 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008230 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008231 return Top::Throw(Heap::illegal_execution_state_symbol());
8232 }
8233
8234 return Heap::true_value();
8235}
8236
8237
8238static Object* Runtime_GetFrameCount(Arguments args) {
8239 HandleScope scope;
8240 ASSERT(args.length() == 1);
8241
8242 // Check arguments.
8243 Object* result = Runtime_CheckExecutionState(args);
8244 if (result->IsFailure()) return result;
8245
8246 // Count all frames which are relevant to debugging stack trace.
8247 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008248 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008249 if (id == StackFrame::NO_ID) {
8250 // If there is no JavaScript stack frame count is 0.
8251 return Smi::FromInt(0);
8252 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008253 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8254 return Smi::FromInt(n);
8255}
8256
8257
8258static const int kFrameDetailsFrameIdIndex = 0;
8259static const int kFrameDetailsReceiverIndex = 1;
8260static const int kFrameDetailsFunctionIndex = 2;
8261static const int kFrameDetailsArgumentCountIndex = 3;
8262static const int kFrameDetailsLocalCountIndex = 4;
8263static const int kFrameDetailsSourcePositionIndex = 5;
8264static const int kFrameDetailsConstructCallIndex = 6;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008265static const int kFrameDetailsAtReturnIndex = 7;
8266static const int kFrameDetailsDebuggerFrameIndex = 8;
8267static const int kFrameDetailsFirstDynamicIndex = 9;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008268
8269// Return an array with frame details
8270// args[0]: number: break id
8271// args[1]: number: frame index
8272//
8273// The array returned contains the following information:
8274// 0: Frame id
8275// 1: Receiver
8276// 2: Function
8277// 3: Argument count
8278// 4: Local count
8279// 5: Source position
8280// 6: Constructor call
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008281// 7: Is at return
8282// 8: Debugger frame
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008283// Arguments name, value
8284// Locals name, value
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008285// Return value if any
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008286static Object* Runtime_GetFrameDetails(Arguments args) {
8287 HandleScope scope;
8288 ASSERT(args.length() == 2);
8289
8290 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008291 Object* check = Runtime_CheckExecutionState(args);
8292 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008293 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8294
8295 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008296 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008297 if (id == StackFrame::NO_ID) {
8298 // If there are no JavaScript stack frames return undefined.
8299 return Heap::undefined_value();
8300 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008301 int count = 0;
8302 JavaScriptFrameIterator it(id);
8303 for (; !it.done(); it.Advance()) {
8304 if (count == index) break;
8305 count++;
8306 }
8307 if (it.done()) return Heap::undefined_value();
8308
8309 // Traverse the saved contexts chain to find the active context for the
8310 // selected frame.
8311 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008312 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008313 save = save->prev();
8314 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008315 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008316
8317 // Get the frame id.
8318 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8319
8320 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008321 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008322
8323 // Check for constructor frame.
8324 bool constructor = it.frame()->IsConstructor();
8325
8326 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008327 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008328 ScopeInfo<> info(*code);
8329
8330 // Get the context.
8331 Handle<Context> context(Context::cast(it.frame()->context()));
8332
8333 // Get the locals names and values into a temporary array.
8334 //
8335 // TODO(1240907): Hide compiler-introduced stack variables
8336 // (e.g. .result)? For users of the debugger, they will probably be
8337 // confusing.
8338 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8339 for (int i = 0; i < info.NumberOfLocals(); i++) {
8340 // Name of the local.
8341 locals->set(i * 2, *info.LocalName(i));
8342
8343 // Fetch the value of the local - either from the stack or from a
8344 // heap-allocated context.
8345 if (i < info.number_of_stack_slots()) {
8346 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8347 } else {
8348 Handle<String> name = info.LocalName(i);
8349 // Traverse the context chain to the function context as all local
8350 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008351 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008352 context = Handle<Context>(context->previous());
8353 }
8354 ASSERT(context->is_function_context());
8355 locals->set(i * 2 + 1,
8356 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8357 NULL)));
8358 }
8359 }
8360
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008361 // Check whether this frame is positioned at return.
8362 int at_return = (index == 0) ? Debug::IsBreakAtReturn(it.frame()) : false;
8363
8364 // If positioned just before return find the value to be returned and add it
8365 // to the frame information.
8366 Handle<Object> return_value = Factory::undefined_value();
8367 if (at_return) {
8368 StackFrameIterator it2;
8369 Address internal_frame_sp = NULL;
8370 while (!it2.done()) {
8371 if (it2.frame()->is_internal()) {
8372 internal_frame_sp = it2.frame()->sp();
8373 } else {
8374 if (it2.frame()->is_java_script()) {
8375 if (it2.frame()->id() == it.frame()->id()) {
8376 // The internal frame just before the JavaScript frame contains the
8377 // value to return on top. A debug break at return will create an
8378 // internal frame to store the return value (eax/rax/r0) before
8379 // entering the debug break exit frame.
8380 if (internal_frame_sp != NULL) {
8381 return_value =
8382 Handle<Object>(Memory::Object_at(internal_frame_sp));
8383 break;
8384 }
8385 }
8386 }
8387
8388 // Indicate that the previous frame was not an internal frame.
8389 internal_frame_sp = NULL;
8390 }
8391 it2.Advance();
8392 }
8393 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008394
8395 // Now advance to the arguments adapter frame (if any). It contains all
8396 // the provided parameters whereas the function frame always have the number
8397 // of arguments matching the functions parameters. The rest of the
8398 // information (except for what is collected above) is the same.
8399 it.AdvanceToArgumentsFrame();
8400
8401 // Find the number of arguments to fill. At least fill the number of
8402 // parameters for the function and fill more if more parameters are provided.
8403 int argument_count = info.number_of_parameters();
8404 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8405 argument_count = it.frame()->GetProvidedParametersCount();
8406 }
8407
8408 // Calculate the size of the result.
8409 int details_size = kFrameDetailsFirstDynamicIndex +
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008410 2 * (argument_count + info.NumberOfLocals()) +
8411 (at_return ? 1 : 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008412 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8413
8414 // Add the frame id.
8415 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8416
8417 // Add the function (same as in function frame).
8418 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8419
8420 // Add the arguments count.
8421 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8422
8423 // Add the locals count
8424 details->set(kFrameDetailsLocalCountIndex,
8425 Smi::FromInt(info.NumberOfLocals()));
8426
8427 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008428 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008429 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8430 } else {
8431 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8432 }
8433
8434 // Add the constructor information.
8435 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8436
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008437 // Add the at return information.
8438 details->set(kFrameDetailsAtReturnIndex, Heap::ToBoolean(at_return));
8439
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008440 // Add information on whether this frame is invoked in the debugger context.
8441 details->set(kFrameDetailsDebuggerFrameIndex,
8442 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8443
8444 // Fill the dynamic part.
8445 int details_index = kFrameDetailsFirstDynamicIndex;
8446
8447 // Add arguments name and value.
8448 for (int i = 0; i < argument_count; i++) {
8449 // Name of the argument.
8450 if (i < info.number_of_parameters()) {
8451 details->set(details_index++, *info.parameter_name(i));
8452 } else {
8453 details->set(details_index++, Heap::undefined_value());
8454 }
8455
8456 // Parameter value.
8457 if (i < it.frame()->GetProvidedParametersCount()) {
8458 details->set(details_index++, it.frame()->GetParameter(i));
8459 } else {
8460 details->set(details_index++, Heap::undefined_value());
8461 }
8462 }
8463
8464 // Add locals name and value from the temporary copy from the function frame.
8465 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8466 details->set(details_index++, locals->get(i));
8467 }
8468
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00008469 // Add the value being returned.
8470 if (at_return) {
8471 details->set(details_index++, *return_value);
8472 }
8473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008474 // Add the receiver (same as in function frame).
8475 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8476 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8477 Handle<Object> receiver(it.frame()->receiver());
8478 if (!receiver->IsJSObject()) {
8479 // If the receiver is NOT a JSObject we have hit an optimization
8480 // where a value object is not converted into a wrapped JS objects.
8481 // To hide this optimization from the debugger, we wrap the receiver
8482 // by creating correct wrapper object based on the calling frame's
8483 // global context.
8484 it.Advance();
8485 Handle<Context> calling_frames_global_context(
8486 Context::cast(Context::cast(it.frame()->context())->global_context()));
8487 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8488 }
8489 details->set(kFrameDetailsReceiverIndex, *receiver);
8490
8491 ASSERT_EQ(details_size, details_index);
8492 return *Factory::NewJSArrayWithElements(details);
8493}
8494
8495
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008496// Copy all the context locals into an object used to materialize a scope.
8497static void CopyContextLocalsToScopeObject(Handle<Code> code,
8498 ScopeInfo<>& scope_info,
8499 Handle<Context> context,
8500 Handle<JSObject> scope_object) {
8501 // Fill all context locals to the context extension.
8502 for (int i = Context::MIN_CONTEXT_SLOTS;
8503 i < scope_info.number_of_context_slots();
8504 i++) {
8505 int context_index =
8506 ScopeInfo<>::ContextSlotIndex(*code,
8507 *scope_info.context_slot_name(i),
8508 NULL);
8509
8510 // Don't include the arguments shadow (.arguments) context variable.
8511 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8512 SetProperty(scope_object,
8513 scope_info.context_slot_name(i),
8514 Handle<Object>(context->get(context_index)), NONE);
8515 }
8516 }
8517}
8518
8519
8520// Create a plain JSObject which materializes the local scope for the specified
8521// frame.
8522static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8523 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8524 Handle<Code> code(function->code());
8525 ScopeInfo<> scope_info(*code);
8526
8527 // Allocate and initialize a JSObject with all the arguments, stack locals
8528 // heap locals and extension properties of the debugged function.
8529 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8530
8531 // First fill all parameters.
8532 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8533 SetProperty(local_scope,
8534 scope_info.parameter_name(i),
8535 Handle<Object>(frame->GetParameter(i)), NONE);
8536 }
8537
8538 // Second fill all stack locals.
8539 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8540 SetProperty(local_scope,
8541 scope_info.stack_slot_name(i),
8542 Handle<Object>(frame->GetExpression(i)), NONE);
8543 }
8544
8545 // Third fill all context locals.
8546 Handle<Context> frame_context(Context::cast(frame->context()));
8547 Handle<Context> function_context(frame_context->fcontext());
8548 CopyContextLocalsToScopeObject(code, scope_info,
8549 function_context, local_scope);
8550
8551 // Finally copy any properties from the function context extension. This will
8552 // be variables introduced by eval.
8553 if (function_context->closure() == *function) {
8554 if (function_context->has_extension() &&
8555 !function_context->IsGlobalContext()) {
8556 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008557 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008558 for (int i = 0; i < keys->length(); i++) {
8559 // Names of variables introduced by eval are strings.
8560 ASSERT(keys->get(i)->IsString());
8561 Handle<String> key(String::cast(keys->get(i)));
8562 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8563 }
8564 }
8565 }
8566 return local_scope;
8567}
8568
8569
8570// Create a plain JSObject which materializes the closure content for the
8571// context.
8572static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8573 ASSERT(context->is_function_context());
8574
8575 Handle<Code> code(context->closure()->code());
8576 ScopeInfo<> scope_info(*code);
8577
8578 // Allocate and initialize a JSObject with all the content of theis function
8579 // closure.
8580 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8581
8582 // Check whether the arguments shadow object exists.
8583 int arguments_shadow_index =
8584 ScopeInfo<>::ContextSlotIndex(*code,
8585 Heap::arguments_shadow_symbol(),
8586 NULL);
8587 if (arguments_shadow_index >= 0) {
8588 // In this case all the arguments are available in the arguments shadow
8589 // object.
8590 Handle<JSObject> arguments_shadow(
8591 JSObject::cast(context->get(arguments_shadow_index)));
8592 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8593 SetProperty(closure_scope,
8594 scope_info.parameter_name(i),
8595 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8596 }
8597 }
8598
8599 // Fill all context locals to the context extension.
8600 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8601
8602 // Finally copy any properties from the function context extension. This will
8603 // be variables introduced by eval.
8604 if (context->has_extension()) {
8605 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008606 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008607 for (int i = 0; i < keys->length(); i++) {
8608 // Names of variables introduced by eval are strings.
8609 ASSERT(keys->get(i)->IsString());
8610 Handle<String> key(String::cast(keys->get(i)));
8611 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8612 }
8613 }
8614
8615 return closure_scope;
8616}
8617
8618
8619// Iterate over the actual scopes visible from a stack frame. All scopes are
8620// backed by an actual context except the local scope, which is inserted
8621// "artifically" in the context chain.
8622class ScopeIterator {
8623 public:
8624 enum ScopeType {
8625 ScopeTypeGlobal = 0,
8626 ScopeTypeLocal,
8627 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008628 ScopeTypeClosure,
8629 // Every catch block contains an implicit with block (its parameter is
8630 // a JSContextExtensionObject) that extends current scope with a variable
8631 // holding exception object. Such with blocks are treated as scopes of their
8632 // own type.
8633 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008634 };
8635
8636 explicit ScopeIterator(JavaScriptFrame* frame)
8637 : frame_(frame),
8638 function_(JSFunction::cast(frame->function())),
8639 context_(Context::cast(frame->context())),
8640 local_done_(false),
8641 at_local_(false) {
8642
8643 // Check whether the first scope is actually a local scope.
8644 if (context_->IsGlobalContext()) {
8645 // If there is a stack slot for .result then this local scope has been
8646 // created for evaluating top level code and it is not a real local scope.
8647 // Checking for the existence of .result seems fragile, but the scope info
8648 // saved with the code object does not otherwise have that information.
8649 Handle<Code> code(function_->code());
8650 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8651 at_local_ = index < 0;
8652 } else if (context_->is_function_context()) {
8653 at_local_ = true;
8654 }
8655 }
8656
8657 // More scopes?
8658 bool Done() { return context_.is_null(); }
8659
8660 // Move to the next scope.
8661 void Next() {
8662 // If at a local scope mark the local scope as passed.
8663 if (at_local_) {
8664 at_local_ = false;
8665 local_done_ = true;
8666
8667 // If the current context is not associated with the local scope the
8668 // current context is the next real scope, so don't move to the next
8669 // context in this case.
8670 if (context_->closure() != *function_) {
8671 return;
8672 }
8673 }
8674
8675 // The global scope is always the last in the chain.
8676 if (context_->IsGlobalContext()) {
8677 context_ = Handle<Context>();
8678 return;
8679 }
8680
8681 // Move to the next context.
8682 if (context_->is_function_context()) {
8683 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8684 } else {
8685 context_ = Handle<Context>(context_->previous());
8686 }
8687
8688 // If passing the local scope indicate that the current scope is now the
8689 // local scope.
8690 if (!local_done_ &&
8691 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8692 at_local_ = true;
8693 }
8694 }
8695
8696 // Return the type of the current scope.
8697 int Type() {
8698 if (at_local_) {
8699 return ScopeTypeLocal;
8700 }
8701 if (context_->IsGlobalContext()) {
8702 ASSERT(context_->global()->IsGlobalObject());
8703 return ScopeTypeGlobal;
8704 }
8705 if (context_->is_function_context()) {
8706 return ScopeTypeClosure;
8707 }
8708 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008709 // Current scope is either an explicit with statement or a with statement
8710 // implicitely generated for a catch block.
8711 // If the extension object here is a JSContextExtensionObject then
8712 // current with statement is one frome a catch block otherwise it's a
8713 // regular with statement.
8714 if (context_->extension()->IsJSContextExtensionObject()) {
8715 return ScopeTypeCatch;
8716 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008717 return ScopeTypeWith;
8718 }
8719
8720 // Return the JavaScript object with the content of the current scope.
8721 Handle<JSObject> ScopeObject() {
8722 switch (Type()) {
8723 case ScopeIterator::ScopeTypeGlobal:
8724 return Handle<JSObject>(CurrentContext()->global());
8725 break;
8726 case ScopeIterator::ScopeTypeLocal:
8727 // Materialize the content of the local scope into a JSObject.
8728 return MaterializeLocalScope(frame_);
8729 break;
8730 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008731 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008732 // Return the with object.
8733 return Handle<JSObject>(CurrentContext()->extension());
8734 break;
8735 case ScopeIterator::ScopeTypeClosure:
8736 // Materialize the content of the closure scope into a JSObject.
8737 return MaterializeClosure(CurrentContext());
8738 break;
8739 }
8740 UNREACHABLE();
8741 return Handle<JSObject>();
8742 }
8743
8744 // Return the context for this scope. For the local context there might not
8745 // be an actual context.
8746 Handle<Context> CurrentContext() {
8747 if (at_local_ && context_->closure() != *function_) {
8748 return Handle<Context>();
8749 }
8750 return context_;
8751 }
8752
8753#ifdef DEBUG
8754 // Debug print of the content of the current scope.
8755 void DebugPrint() {
8756 switch (Type()) {
8757 case ScopeIterator::ScopeTypeGlobal:
8758 PrintF("Global:\n");
8759 CurrentContext()->Print();
8760 break;
8761
8762 case ScopeIterator::ScopeTypeLocal: {
8763 PrintF("Local:\n");
8764 Handle<Code> code(function_->code());
8765 ScopeInfo<> scope_info(*code);
8766 scope_info.Print();
8767 if (!CurrentContext().is_null()) {
8768 CurrentContext()->Print();
8769 if (CurrentContext()->has_extension()) {
8770 Handle<JSObject> extension =
8771 Handle<JSObject>(CurrentContext()->extension());
8772 if (extension->IsJSContextExtensionObject()) {
8773 extension->Print();
8774 }
8775 }
8776 }
8777 break;
8778 }
8779
8780 case ScopeIterator::ScopeTypeWith: {
8781 PrintF("With:\n");
8782 Handle<JSObject> extension =
8783 Handle<JSObject>(CurrentContext()->extension());
8784 extension->Print();
8785 break;
8786 }
8787
ager@chromium.orga1645e22009-09-09 19:27:10 +00008788 case ScopeIterator::ScopeTypeCatch: {
8789 PrintF("Catch:\n");
8790 Handle<JSObject> extension =
8791 Handle<JSObject>(CurrentContext()->extension());
8792 extension->Print();
8793 break;
8794 }
8795
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008796 case ScopeIterator::ScopeTypeClosure: {
8797 PrintF("Closure:\n");
8798 CurrentContext()->Print();
8799 if (CurrentContext()->has_extension()) {
8800 Handle<JSObject> extension =
8801 Handle<JSObject>(CurrentContext()->extension());
8802 if (extension->IsJSContextExtensionObject()) {
8803 extension->Print();
8804 }
8805 }
8806 break;
8807 }
8808
8809 default:
8810 UNREACHABLE();
8811 }
8812 PrintF("\n");
8813 }
8814#endif
8815
8816 private:
8817 JavaScriptFrame* frame_;
8818 Handle<JSFunction> function_;
8819 Handle<Context> context_;
8820 bool local_done_;
8821 bool at_local_;
8822
8823 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8824};
8825
8826
8827static Object* Runtime_GetScopeCount(Arguments args) {
8828 HandleScope scope;
8829 ASSERT(args.length() == 2);
8830
8831 // Check arguments.
8832 Object* check = Runtime_CheckExecutionState(args);
8833 if (check->IsFailure()) return check;
8834 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8835
8836 // Get the frame where the debugging is performed.
8837 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8838 JavaScriptFrameIterator it(id);
8839 JavaScriptFrame* frame = it.frame();
8840
8841 // Count the visible scopes.
8842 int n = 0;
8843 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8844 n++;
8845 }
8846
8847 return Smi::FromInt(n);
8848}
8849
8850
8851static const int kScopeDetailsTypeIndex = 0;
8852static const int kScopeDetailsObjectIndex = 1;
8853static const int kScopeDetailsSize = 2;
8854
8855// Return an array with scope details
8856// args[0]: number: break id
8857// args[1]: number: frame index
8858// args[2]: number: scope index
8859//
8860// The array returned contains the following information:
8861// 0: Scope type
8862// 1: Scope object
8863static Object* Runtime_GetScopeDetails(Arguments args) {
8864 HandleScope scope;
8865 ASSERT(args.length() == 3);
8866
8867 // Check arguments.
8868 Object* check = Runtime_CheckExecutionState(args);
8869 if (check->IsFailure()) return check;
8870 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8871 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8872
8873 // Get the frame where the debugging is performed.
8874 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8875 JavaScriptFrameIterator frame_it(id);
8876 JavaScriptFrame* frame = frame_it.frame();
8877
8878 // Find the requested scope.
8879 int n = 0;
8880 ScopeIterator it(frame);
8881 for (; !it.Done() && n < index; it.Next()) {
8882 n++;
8883 }
8884 if (it.Done()) {
8885 return Heap::undefined_value();
8886 }
8887
8888 // Calculate the size of the result.
8889 int details_size = kScopeDetailsSize;
8890 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8891
8892 // Fill in scope details.
8893 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8894 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8895
8896 return *Factory::NewJSArrayWithElements(details);
8897}
8898
8899
8900static Object* Runtime_DebugPrintScopes(Arguments args) {
8901 HandleScope scope;
8902 ASSERT(args.length() == 0);
8903
8904#ifdef DEBUG
8905 // Print the scopes for the top frame.
8906 StackFrameLocator locator;
8907 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8908 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8909 it.DebugPrint();
8910 }
8911#endif
8912 return Heap::undefined_value();
8913}
8914
8915
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008916static Object* Runtime_GetCFrames(Arguments args) {
8917 HandleScope scope;
8918 ASSERT(args.length() == 1);
8919 Object* result = Runtime_CheckExecutionState(args);
8920 if (result->IsFailure()) return result;
8921
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008922#if V8_HOST_ARCH_64_BIT
8923 UNIMPLEMENTED();
8924 return Heap::undefined_value();
8925#else
8926
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008927 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008928 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8929 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008930 if (frames_count == OS::kStackWalkError) {
8931 return Heap::undefined_value();
8932 }
8933
8934 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8935 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8936 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8937 for (int i = 0; i < frames_count; i++) {
8938 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8939 frame_value->SetProperty(
8940 *address_str,
8941 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8942 NONE);
8943
8944 // Get the stack walk text for this frame.
8945 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008946 int frame_text_length = StrLength(frames[i].text);
8947 if (frame_text_length > 0) {
8948 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008949 frame_text = Factory::NewStringFromAscii(str);
8950 }
8951
8952 if (!frame_text.is_null()) {
8953 frame_value->SetProperty(*text_str, *frame_text, NONE);
8954 }
8955
8956 frames_array->set(i, *frame_value);
8957 }
8958 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008959#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008960}
8961
8962
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008963static Object* Runtime_GetThreadCount(Arguments args) {
8964 HandleScope scope;
8965 ASSERT(args.length() == 1);
8966
8967 // Check arguments.
8968 Object* result = Runtime_CheckExecutionState(args);
8969 if (result->IsFailure()) return result;
8970
8971 // Count all archived V8 threads.
8972 int n = 0;
8973 for (ThreadState* thread = ThreadState::FirstInUse();
8974 thread != NULL;
8975 thread = thread->Next()) {
8976 n++;
8977 }
8978
8979 // Total number of threads is current thread and archived threads.
8980 return Smi::FromInt(n + 1);
8981}
8982
8983
8984static const int kThreadDetailsCurrentThreadIndex = 0;
8985static const int kThreadDetailsThreadIdIndex = 1;
8986static const int kThreadDetailsSize = 2;
8987
8988// Return an array with thread details
8989// args[0]: number: break id
8990// args[1]: number: thread index
8991//
8992// The array returned contains the following information:
8993// 0: Is current thread?
8994// 1: Thread id
8995static Object* Runtime_GetThreadDetails(Arguments args) {
8996 HandleScope scope;
8997 ASSERT(args.length() == 2);
8998
8999 // Check arguments.
9000 Object* check = Runtime_CheckExecutionState(args);
9001 if (check->IsFailure()) return check;
9002 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
9003
9004 // Allocate array for result.
9005 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
9006
9007 // Thread index 0 is current thread.
9008 if (index == 0) {
9009 // Fill the details.
9010 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
9011 details->set(kThreadDetailsThreadIdIndex,
9012 Smi::FromInt(ThreadManager::CurrentId()));
9013 } else {
9014 // Find the thread with the requested index.
9015 int n = 1;
9016 ThreadState* thread = ThreadState::FirstInUse();
9017 while (index != n && thread != NULL) {
9018 thread = thread->Next();
9019 n++;
9020 }
9021 if (thread == NULL) {
9022 return Heap::undefined_value();
9023 }
9024
9025 // Fill the details.
9026 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
9027 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
9028 }
9029
9030 // Convert to JS array and return.
9031 return *Factory::NewJSArrayWithElements(details);
9032}
9033
9034
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009035static Object* Runtime_GetBreakLocations(Arguments args) {
9036 HandleScope scope;
9037 ASSERT(args.length() == 1);
9038
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009039 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9040 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009041 // Find the number of break points
9042 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
9043 if (break_locations->IsUndefined()) return Heap::undefined_value();
9044 // Return array as JS array
9045 return *Factory::NewJSArrayWithElements(
9046 Handle<FixedArray>::cast(break_locations));
9047}
9048
9049
9050// Set a break point in a function
9051// args[0]: function
9052// args[1]: number: break source position (within the function source)
9053// args[2]: number: break point object
9054static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
9055 HandleScope scope;
9056 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00009057 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
9058 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009059 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9060 RUNTIME_ASSERT(source_position >= 0);
9061 Handle<Object> break_point_object_arg = args.at<Object>(2);
9062
9063 // Set break point.
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009064 Debug::SetBreakPoint(shared, break_point_object_arg, &source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009065
9066 return Heap::undefined_value();
9067}
9068
9069
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009070Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
9071 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009072 // Iterate the heap looking for SharedFunctionInfo generated from the
9073 // script. The inner most SharedFunctionInfo containing the source position
9074 // for the requested break point is found.
9075 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
9076 // which is found is not compiled it is compiled and the heap is iterated
9077 // again as the compilation might create inner functions from the newly
9078 // compiled function and the actual requested break point might be in one of
9079 // these functions.
9080 bool done = false;
9081 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00009082 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009083 Handle<SharedFunctionInfo> target;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009084 while (!done) {
9085 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009086 for (HeapObject* obj = iterator.next();
9087 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009088 if (obj->IsSharedFunctionInfo()) {
9089 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
9090 if (shared->script() == *script) {
9091 // If the SharedFunctionInfo found has the requested script data and
9092 // contains the source position it is a candidate.
9093 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00009094 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009095 start_position = shared->start_position();
9096 }
9097 if (start_position <= position &&
9098 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00009099 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009100 // candidate this is the new candidate.
9101 if (target.is_null()) {
9102 target_start_position = start_position;
9103 target = shared;
9104 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00009105 if (target_start_position == start_position &&
9106 shared->end_position() == target->end_position()) {
9107 // If a top-level function contain only one function
9108 // declartion the source for the top-level and the function is
9109 // the same. In that case prefer the non top-level function.
9110 if (!shared->is_toplevel()) {
9111 target_start_position = start_position;
9112 target = shared;
9113 }
9114 } else if (target_start_position <= start_position &&
9115 shared->end_position() <= target->end_position()) {
9116 // This containment check includes equality as a function inside
9117 // a top-level function can share either start or end position
9118 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009119 target_start_position = start_position;
9120 target = shared;
9121 }
9122 }
9123 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009124 }
9125 }
9126 }
9127
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009128 if (target.is_null()) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009129 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009130 }
9131
9132 // If the candidate found is compiled we are done. NOTE: when lazy
9133 // compilation of inner functions is introduced some additional checking
9134 // needs to be done here to compile inner functions.
9135 done = target->is_compiled();
9136 if (!done) {
9137 // If the candidate is not compiled compile it to reveal any inner
9138 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009139 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009140 }
9141 }
9142
9143 return *target;
9144}
9145
9146
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009147// Changes the state of a break point in a script and returns source position
9148// where break point was set. NOTE: Regarding performance see the NOTE for
9149// GetScriptFromScriptData.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009150// args[0]: script to set break point in
9151// args[1]: number: break source position (within the script source)
9152// args[2]: number: break point object
9153static Object* Runtime_SetScriptBreakPoint(Arguments args) {
9154 HandleScope scope;
9155 ASSERT(args.length() == 3);
9156 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
9157 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9158 RUNTIME_ASSERT(source_position >= 0);
9159 Handle<Object> break_point_object_arg = args.at<Object>(2);
9160
9161 // Get the script from the script wrapper.
9162 RUNTIME_ASSERT(wrapper->value()->IsScript());
9163 Handle<Script> script(Script::cast(wrapper->value()));
9164
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009165 Object* result = Runtime::FindSharedFunctionInfoInScript(
9166 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009167 if (!result->IsUndefined()) {
9168 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9169 // Find position within function. The script position might be before the
9170 // source position of the first function.
9171 int position;
9172 if (shared->start_position() > source_position) {
9173 position = 0;
9174 } else {
9175 position = source_position - shared->start_position();
9176 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00009177 Debug::SetBreakPoint(shared, break_point_object_arg, &position);
9178 position += shared->start_position();
9179 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009180 }
9181 return Heap::undefined_value();
9182}
9183
9184
9185// Clear a break point
9186// args[0]: number: break point object
9187static Object* Runtime_ClearBreakPoint(Arguments args) {
9188 HandleScope scope;
9189 ASSERT(args.length() == 1);
9190 Handle<Object> break_point_object_arg = args.at<Object>(0);
9191
9192 // Clear break point.
9193 Debug::ClearBreakPoint(break_point_object_arg);
9194
9195 return Heap::undefined_value();
9196}
9197
9198
9199// Change the state of break on exceptions
9200// args[0]: boolean indicating uncaught exceptions
9201// args[1]: boolean indicating on/off
9202static Object* Runtime_ChangeBreakOnException(Arguments args) {
9203 HandleScope scope;
9204 ASSERT(args.length() == 2);
9205 ASSERT(args[0]->IsNumber());
9206 ASSERT(args[1]->IsBoolean());
9207
9208 // Update break point state
9209 ExceptionBreakType type =
9210 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9211 bool enable = args[1]->ToBoolean()->IsTrue();
9212 Debug::ChangeBreakOnException(type, enable);
9213 return Heap::undefined_value();
9214}
9215
9216
9217// Prepare for stepping
9218// args[0]: break id for checking execution state
9219// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009220// args[2]: number of times to perform the step, for step out it is the number
9221// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009222static Object* Runtime_PrepareStep(Arguments args) {
9223 HandleScope scope;
9224 ASSERT(args.length() == 3);
9225 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009226 Object* check = Runtime_CheckExecutionState(args);
9227 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009228 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9229 return Top::Throw(Heap::illegal_argument_symbol());
9230 }
9231
9232 // Get the step action and check validity.
9233 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9234 if (step_action != StepIn &&
9235 step_action != StepNext &&
9236 step_action != StepOut &&
9237 step_action != StepInMin &&
9238 step_action != StepMin) {
9239 return Top::Throw(Heap::illegal_argument_symbol());
9240 }
9241
9242 // Get the number of steps.
9243 int step_count = NumberToInt32(args[2]);
9244 if (step_count < 1) {
9245 return Top::Throw(Heap::illegal_argument_symbol());
9246 }
9247
ager@chromium.orga1645e22009-09-09 19:27:10 +00009248 // Clear all current stepping setup.
9249 Debug::ClearStepping();
9250
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009251 // Prepare step.
9252 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9253 return Heap::undefined_value();
9254}
9255
9256
9257// Clear all stepping set by PrepareStep.
9258static Object* Runtime_ClearStepping(Arguments args) {
9259 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009260 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009261 Debug::ClearStepping();
9262 return Heap::undefined_value();
9263}
9264
9265
9266// Creates a copy of the with context chain. The copy of the context chain is
9267// is linked to the function context supplied.
9268static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9269 Handle<Context> function_context) {
9270 // At the bottom of the chain. Return the function context to link to.
9271 if (context_chain->is_function_context()) {
9272 return function_context;
9273 }
9274
9275 // Recursively copy the with contexts.
9276 Handle<Context> previous(context_chain->previous());
9277 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9278 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009279 CopyWithContextChain(function_context, previous),
9280 extension,
9281 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009282}
9283
9284
9285// Helper function to find or create the arguments object for
9286// Runtime_DebugEvaluate.
9287static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9288 Handle<JSFunction> function,
9289 Handle<Code> code,
9290 const ScopeInfo<>* sinfo,
9291 Handle<Context> function_context) {
9292 // Try to find the value of 'arguments' to pass as parameter. If it is not
9293 // found (that is the debugged function does not reference 'arguments' and
9294 // does not support eval) then create an 'arguments' object.
9295 int index;
9296 if (sinfo->number_of_stack_slots() > 0) {
9297 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9298 if (index != -1) {
9299 return Handle<Object>(frame->GetExpression(index));
9300 }
9301 }
9302
9303 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9304 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9305 NULL);
9306 if (index != -1) {
9307 return Handle<Object>(function_context->get(index));
9308 }
9309 }
9310
9311 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009312 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9313 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009314
9315 AssertNoAllocation no_gc;
9316 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009317 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009318 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009319 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009320 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009321 return arguments;
9322}
9323
9324
9325// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009326// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009327// extension part has all the parameters and locals of the function on the
9328// stack frame. A function which calls eval with the code to evaluate is then
9329// compiled in this context and called in this context. As this context
9330// replaces the context of the function on the stack frame a new (empty)
9331// function is created as well to be used as the closure for the context.
9332// This function and the context acts as replacements for the function on the
9333// stack frame presenting the same view of the values of parameters and
9334// local variables as if the piece of JavaScript was evaluated at the point
9335// where the function on the stack frame is currently stopped.
9336static Object* Runtime_DebugEvaluate(Arguments args) {
9337 HandleScope scope;
9338
9339 // Check the execution state and decode arguments frame and source to be
9340 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009341 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009342 Object* check_result = Runtime_CheckExecutionState(args);
9343 if (check_result->IsFailure()) return check_result;
9344 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9345 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009346 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9347
9348 // Handle the processing of break.
9349 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009350
9351 // Get the frame where the debugging is performed.
9352 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9353 JavaScriptFrameIterator it(id);
9354 JavaScriptFrame* frame = it.frame();
9355 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9356 Handle<Code> code(function->code());
9357 ScopeInfo<> sinfo(*code);
9358
9359 // Traverse the saved contexts chain to find the active context for the
9360 // selected frame.
9361 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009362 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009363 save = save->prev();
9364 }
9365 ASSERT(save != NULL);
9366 SaveContext savex;
9367 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009368
9369 // Create the (empty) function replacing the function on the stack frame for
9370 // the purpose of evaluating in the context created below. It is important
9371 // that this function does not describe any parameters and local variables
9372 // in the context. If it does then this will cause problems with the lookup
9373 // in Context::Lookup, where context slots for parameters and local variables
9374 // are looked at before the extension object.
9375 Handle<JSFunction> go_between =
9376 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9377 go_between->set_context(function->context());
9378#ifdef DEBUG
9379 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9380 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9381 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9382#endif
9383
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009384 // Materialize the content of the local scope into a JSObject.
9385 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009386
9387 // Allocate a new context for the debug evaluation and set the extension
9388 // object build.
9389 Handle<Context> context =
9390 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009391 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009392 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009393 Handle<Context> frame_context(Context::cast(frame->context()));
9394 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009395 context = CopyWithContextChain(frame_context, context);
9396
9397 // Wrap the evaluation statement in a new function compiled in the newly
9398 // created context. The function has one parameter which has to be called
9399 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009400 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009401 // function(arguments,__source__) {return eval(__source__);}
9402 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009403 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009404 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009405 Handle<String> function_source =
9406 Factory::NewStringFromAscii(Vector<const char>(source_str,
9407 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009408 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009409 Compiler::CompileEval(function_source,
9410 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009411 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009412 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009413 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009414 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009415 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009416
9417 // Invoke the result of the compilation to get the evaluation function.
9418 bool has_pending_exception;
9419 Handle<Object> receiver(frame->receiver());
9420 Handle<Object> evaluation_function =
9421 Execution::Call(compiled_function, receiver, 0, NULL,
9422 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009423 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009424
9425 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9426 function_context);
9427
9428 // Invoke the evaluation function and return the result.
9429 const int argc = 2;
9430 Object** argv[argc] = { arguments.location(),
9431 Handle<Object>::cast(source).location() };
9432 Handle<Object> result =
9433 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9434 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009435 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009436
9437 // Skip the global proxy as it has no properties and always delegates to the
9438 // real global object.
9439 if (result->IsJSGlobalProxy()) {
9440 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9441 }
9442
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009443 return *result;
9444}
9445
9446
9447static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9448 HandleScope scope;
9449
9450 // Check the execution state and decode arguments frame and source to be
9451 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009452 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009453 Object* check_result = Runtime_CheckExecutionState(args);
9454 if (check_result->IsFailure()) return check_result;
9455 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009456 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9457
9458 // Handle the processing of break.
9459 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009460
9461 // Enter the top context from before the debugger was invoked.
9462 SaveContext save;
9463 SaveContext* top = &save;
9464 while (top != NULL && *top->context() == *Debug::debug_context()) {
9465 top = top->prev();
9466 }
9467 if (top != NULL) {
9468 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009469 }
9470
9471 // Get the global context now set to the top context from before the
9472 // debugger was invoked.
9473 Handle<Context> context = Top::global_context();
9474
9475 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009476 Handle<SharedFunctionInfo> shared =
9477 Compiler::CompileEval(source,
9478 context,
9479 true,
9480 Compiler::DONT_VALIDATE_JSON);
9481 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009482 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009483 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9484 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009485
9486 // Invoke the result of the compilation to get the evaluation function.
9487 bool has_pending_exception;
9488 Handle<Object> receiver = Top::global();
9489 Handle<Object> result =
9490 Execution::Call(compiled_function, receiver, 0, NULL,
9491 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009492 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009493 return *result;
9494}
9495
9496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009497static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9498 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009499 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009501 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009502 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009503
9504 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009505 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009506 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9507 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9508 // because using
9509 // instances->set(i, *GetScriptWrapper(script))
9510 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9511 // already have deferenced the instances handle.
9512 Handle<JSValue> wrapper = GetScriptWrapper(script);
9513 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009514 }
9515
9516 // Return result as a JS array.
9517 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9518 Handle<JSArray>::cast(result)->SetContent(*instances);
9519 return *result;
9520}
9521
9522
9523// Helper function used by Runtime_DebugReferencedBy below.
9524static int DebugReferencedBy(JSObject* target,
9525 Object* instance_filter, int max_references,
9526 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009527 JSFunction* arguments_function) {
9528 NoHandleAllocation ha;
9529 AssertNoAllocation no_alloc;
9530
9531 // Iterate the heap.
9532 int count = 0;
9533 JSObject* last = NULL;
9534 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009535 HeapObject* heap_obj = NULL;
9536 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009537 (max_references == 0 || count < max_references)) {
9538 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009539 if (heap_obj->IsJSObject()) {
9540 // Skip context extension objects and argument arrays as these are
9541 // checked in the context of functions using them.
9542 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009543 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009544 obj->map()->constructor() == arguments_function) {
9545 continue;
9546 }
9547
9548 // Check if the JS object has a reference to the object looked for.
9549 if (obj->ReferencesObject(target)) {
9550 // Check instance filter if supplied. This is normally used to avoid
9551 // references from mirror objects (see Runtime_IsInPrototypeChain).
9552 if (!instance_filter->IsUndefined()) {
9553 Object* V = obj;
9554 while (true) {
9555 Object* prototype = V->GetPrototype();
9556 if (prototype->IsNull()) {
9557 break;
9558 }
9559 if (instance_filter == prototype) {
9560 obj = NULL; // Don't add this object.
9561 break;
9562 }
9563 V = prototype;
9564 }
9565 }
9566
9567 if (obj != NULL) {
9568 // Valid reference found add to instance array if supplied an update
9569 // count.
9570 if (instances != NULL && count < instances_size) {
9571 instances->set(count, obj);
9572 }
9573 last = obj;
9574 count++;
9575 }
9576 }
9577 }
9578 }
9579
9580 // Check for circular reference only. This can happen when the object is only
9581 // referenced from mirrors and has a circular reference in which case the
9582 // object is not really alive and would have been garbage collected if not
9583 // referenced from the mirror.
9584 if (count == 1 && last == target) {
9585 count = 0;
9586 }
9587
9588 // Return the number of referencing objects found.
9589 return count;
9590}
9591
9592
9593// Scan the heap for objects with direct references to an object
9594// args[0]: the object to find references to
9595// args[1]: constructor function for instances to exclude (Mirror)
9596// args[2]: the the maximum number of objects to return
9597static Object* Runtime_DebugReferencedBy(Arguments args) {
9598 ASSERT(args.length() == 3);
9599
9600 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009601 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009602
9603 // Check parameters.
9604 CONVERT_CHECKED(JSObject, target, args[0]);
9605 Object* instance_filter = args[1];
9606 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9607 instance_filter->IsJSObject());
9608 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9609 RUNTIME_ASSERT(max_references >= 0);
9610
9611 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009612 JSObject* arguments_boilerplate =
9613 Top::context()->global_context()->arguments_boilerplate();
9614 JSFunction* arguments_function =
9615 JSFunction::cast(arguments_boilerplate->map()->constructor());
9616
9617 // Get the number of referencing objects.
9618 int count;
9619 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009620 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009621
9622 // Allocate an array to hold the result.
9623 Object* object = Heap::AllocateFixedArray(count);
9624 if (object->IsFailure()) return object;
9625 FixedArray* instances = FixedArray::cast(object);
9626
9627 // Fill the referencing objects.
9628 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009629 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009630
9631 // Return result as JS array.
9632 Object* result =
9633 Heap::AllocateJSObject(
9634 Top::context()->global_context()->array_function());
9635 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9636 return result;
9637}
9638
9639
9640// Helper function used by Runtime_DebugConstructedBy below.
9641static int DebugConstructedBy(JSFunction* constructor, int max_references,
9642 FixedArray* instances, int instances_size) {
9643 AssertNoAllocation no_alloc;
9644
9645 // Iterate the heap.
9646 int count = 0;
9647 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009648 HeapObject* heap_obj = NULL;
9649 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009650 (max_references == 0 || count < max_references)) {
9651 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009652 if (heap_obj->IsJSObject()) {
9653 JSObject* obj = JSObject::cast(heap_obj);
9654 if (obj->map()->constructor() == constructor) {
9655 // Valid reference found add to instance array if supplied an update
9656 // count.
9657 if (instances != NULL && count < instances_size) {
9658 instances->set(count, obj);
9659 }
9660 count++;
9661 }
9662 }
9663 }
9664
9665 // Return the number of referencing objects found.
9666 return count;
9667}
9668
9669
9670// Scan the heap for objects constructed by a specific function.
9671// args[0]: the constructor to find instances of
9672// args[1]: the the maximum number of objects to return
9673static Object* Runtime_DebugConstructedBy(Arguments args) {
9674 ASSERT(args.length() == 2);
9675
9676 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009677 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009678
9679 // Check parameters.
9680 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9681 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9682 RUNTIME_ASSERT(max_references >= 0);
9683
9684 // Get the number of referencing objects.
9685 int count;
9686 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9687
9688 // Allocate an array to hold the result.
9689 Object* object = Heap::AllocateFixedArray(count);
9690 if (object->IsFailure()) return object;
9691 FixedArray* instances = FixedArray::cast(object);
9692
9693 // Fill the referencing objects.
9694 count = DebugConstructedBy(constructor, max_references, instances, count);
9695
9696 // Return result as JS array.
9697 Object* result =
9698 Heap::AllocateJSObject(
9699 Top::context()->global_context()->array_function());
9700 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9701 return result;
9702}
9703
9704
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009705// Find the effective prototype object as returned by __proto__.
9706// args[0]: the object to find the prototype for.
9707static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009708 ASSERT(args.length() == 1);
9709
9710 CONVERT_CHECKED(JSObject, obj, args[0]);
9711
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009712 // Use the __proto__ accessor.
9713 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009714}
9715
9716
9717static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009718 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009719 CPU::DebugBreak();
9720 return Heap::undefined_value();
9721}
9722
9723
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009724static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009725#ifdef DEBUG
9726 HandleScope scope;
9727 ASSERT(args.length() == 1);
9728 // Get the function and make sure it is compiled.
9729 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009730 Handle<SharedFunctionInfo> shared(func->shared());
9731 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009732 return Failure::Exception();
9733 }
9734 func->code()->PrintLn();
9735#endif // DEBUG
9736 return Heap::undefined_value();
9737}
ager@chromium.org9085a012009-05-11 19:22:57 +00009738
9739
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009740static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9741#ifdef DEBUG
9742 HandleScope scope;
9743 ASSERT(args.length() == 1);
9744 // Get the function and make sure it is compiled.
9745 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009746 Handle<SharedFunctionInfo> shared(func->shared());
9747 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009748 return Failure::Exception();
9749 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009750 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009751#endif // DEBUG
9752 return Heap::undefined_value();
9753}
9754
9755
ager@chromium.org9085a012009-05-11 19:22:57 +00009756static Object* Runtime_FunctionGetInferredName(Arguments args) {
9757 NoHandleAllocation ha;
9758 ASSERT(args.length() == 1);
9759
9760 CONVERT_CHECKED(JSFunction, f, args[0]);
9761 return f->shared()->inferred_name();
9762}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009763
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009764
9765static int FindSharedFunctionInfosForScript(Script* script,
9766 FixedArray* buffer) {
9767 AssertNoAllocation no_allocations;
9768
9769 int counter = 0;
9770 int buffer_size = buffer->length();
9771 HeapIterator iterator;
9772 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9773 ASSERT(obj != NULL);
9774 if (!obj->IsSharedFunctionInfo()) {
9775 continue;
9776 }
9777 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9778 if (shared->script() != script) {
9779 continue;
9780 }
9781 if (counter < buffer_size) {
9782 buffer->set(counter, shared);
9783 }
9784 counter++;
9785 }
9786 return counter;
9787}
9788
9789// For a script finds all SharedFunctionInfo's in the heap that points
9790// to this script. Returns JSArray of SharedFunctionInfo wrapped
9791// in OpaqueReferences.
9792static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9793 Arguments args) {
9794 ASSERT(args.length() == 1);
9795 HandleScope scope;
9796 CONVERT_CHECKED(JSValue, script_value, args[0]);
9797
9798 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9799
9800 const int kBufferSize = 32;
9801
9802 Handle<FixedArray> array;
9803 array = Factory::NewFixedArray(kBufferSize);
9804 int number = FindSharedFunctionInfosForScript(*script, *array);
9805 if (number > kBufferSize) {
9806 array = Factory::NewFixedArray(number);
9807 FindSharedFunctionInfosForScript(*script, *array);
9808 }
9809
9810 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9811 result->set_length(Smi::FromInt(number));
9812
9813 LiveEdit::WrapSharedFunctionInfos(result);
9814
9815 return *result;
9816}
9817
9818// For a script calculates compilation information about all its functions.
9819// The script source is explicitly specified by the second argument.
9820// The source of the actual script is not used, however it is important that
9821// all generated code keeps references to this particular instance of script.
9822// Returns a JSArray of compilation infos. The array is ordered so that
9823// each function with all its descendant is always stored in a continues range
9824// with the function itself going first. The root function is a script function.
9825static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9826 ASSERT(args.length() == 2);
9827 HandleScope scope;
9828 CONVERT_CHECKED(JSValue, script, args[0]);
9829 CONVERT_ARG_CHECKED(String, source, 1);
9830 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9831
9832 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9833
9834 if (Top::has_pending_exception()) {
9835 return Failure::Exception();
9836 }
9837
9838 return result;
9839}
9840
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009841// Changes the source of the script to a new_source.
9842// If old_script_name is provided (i.e. is a String), also creates a copy of
9843// the script with its original source and sends notification to debugger.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009844static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9845 ASSERT(args.length() == 3);
9846 HandleScope scope;
9847 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9848 CONVERT_ARG_CHECKED(String, new_source, 1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009849 Handle<Object> old_script_name(args[2]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009850
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009851 CONVERT_CHECKED(Script, original_script_pointer,
9852 original_script_value->value());
9853 Handle<Script> original_script(original_script_pointer);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009854
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009855 Object* old_script = LiveEdit::ChangeScriptSource(original_script,
9856 new_source,
9857 old_script_name);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009858
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009859 if (old_script->IsScript()) {
9860 Handle<Script> script_handle(Script::cast(old_script));
9861 return *(GetScriptWrapper(script_handle));
9862 } else {
9863 return Heap::null_value();
9864 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009865}
9866
9867// Replaces code of SharedFunctionInfo with a new one.
9868static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9869 ASSERT(args.length() == 2);
9870 HandleScope scope;
9871 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9872 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9873
ager@chromium.orgac091b72010-05-05 07:34:42 +00009874 return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009875}
9876
9877// Connects SharedFunctionInfo to another script.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009878static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009879 ASSERT(args.length() == 2);
9880 HandleScope scope;
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009881 Handle<Object> function_object(args[0]);
9882 Handle<Object> script_object(args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009883
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009884 if (function_object->IsJSValue()) {
9885 Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
9886 if (script_object->IsJSValue()) {
9887 CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
9888 script_object = Handle<Object>(script);
9889 }
9890
9891 LiveEdit::SetFunctionScript(function_wrapper, script_object);
9892 } else {
9893 // Just ignore this. We may not have a SharedFunctionInfo for some functions
9894 // and we check it in this function.
9895 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009896
9897 return Heap::undefined_value();
9898}
9899
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00009900
9901// In a code of a parent function replaces original function as embedded object
9902// with a substitution one.
9903static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
9904 ASSERT(args.length() == 3);
9905 HandleScope scope;
9906
9907 CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
9908 CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
9909 CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
9910
9911 LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
9912 subst_wrapper);
9913
9914 return Heap::undefined_value();
9915}
9916
9917
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009918// Updates positions of a shared function info (first parameter) according
9919// to script source change. Text change is described in second parameter as
9920// array of groups of 3 numbers:
9921// (change_begin, change_end, change_end_new_position).
9922// Each group describes a change in text; groups are sorted by change_begin.
9923static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9924 ASSERT(args.length() == 2);
9925 HandleScope scope;
9926 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9927 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9928
ager@chromium.orgac091b72010-05-05 07:34:42 +00009929 return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009930}
9931
9932
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009933// For array of SharedFunctionInfo's (each wrapped in JSValue)
9934// checks that none of them have activations on stacks (of any thread).
9935// Returns array of the same length with corresponding results of
9936// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009937static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9938 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009939 HandleScope scope;
9940 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009941 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009942
ager@chromium.org357bf652010-04-12 11:30:10 +00009943 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009944}
9945
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009946// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009947// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009948static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9949 ASSERT(args.length() == 2);
9950 HandleScope scope;
9951 CONVERT_ARG_CHECKED(String, s1, 0);
9952 CONVERT_ARG_CHECKED(String, s2, 1);
9953
9954 return *LiveEdit::CompareStringsLinewise(s1, s2);
9955}
9956
9957
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009958
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009959// A testing entry. Returns statement position which is the closest to
9960// source_position.
9961static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9962 ASSERT(args.length() == 2);
9963 HandleScope scope;
9964 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9965 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9966
9967 Handle<Code> code(function->code());
9968
9969 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9970 int closest_pc = 0;
9971 int distance = kMaxInt;
9972 while (!it.done()) {
9973 int statement_position = static_cast<int>(it.rinfo()->data());
9974 // Check if this break point is closer that what was previously found.
9975 if (source_position <= statement_position &&
9976 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009977 closest_pc =
9978 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009979 distance = statement_position - source_position;
9980 // Check whether we can't get any closer.
9981 if (distance == 0) break;
9982 }
9983 it.next();
9984 }
9985
9986 return Smi::FromInt(closest_pc);
9987}
9988
9989
ager@chromium.org357bf652010-04-12 11:30:10 +00009990// Calls specified function with or without entering the debugger.
9991// This is used in unit tests to run code as if debugger is entered or simply
9992// to have a stack with C++ frame in the middle.
9993static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9994 ASSERT(args.length() == 2);
9995 HandleScope scope;
9996 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9997 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9998
9999 Handle<Object> result;
10000 bool pending_exception;
10001 {
10002 if (without_debugger) {
10003 result = Execution::Call(function, Top::global(), 0, NULL,
10004 &pending_exception);
10005 } else {
10006 EnterDebugger enter_debugger;
10007 result = Execution::Call(function, Top::global(), 0, NULL,
10008 &pending_exception);
10009 }
10010 }
10011 if (!pending_exception) {
10012 return *result;
10013 } else {
10014 return Failure::Exception();
10015 }
10016}
10017
10018
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010019#endif // ENABLE_DEBUGGER_SUPPORT
10020
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010021#ifdef ENABLE_LOGGING_AND_PROFILING
10022
10023static Object* Runtime_ProfilerResume(Arguments args) {
10024 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010025 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010026
10027 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010028 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10029 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010030 return Heap::undefined_value();
10031}
10032
10033
10034static Object* Runtime_ProfilerPause(Arguments args) {
10035 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +000010036 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010037
10038 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +000010039 CONVERT_CHECKED(Smi, smi_tag, args[1]);
10040 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000010041 return Heap::undefined_value();
10042}
10043
10044#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +000010045
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010046// Finds the script object from the script data. NOTE: This operation uses
10047// heap traversal to find the function generated for the source position
10048// for the requested break point. For lazily compiled functions several heap
10049// traversals might be required rendering this operation as a rather slow
10050// operation. However for setting break points which is normally done through
10051// some kind of user interaction the performance is not crucial.
10052static Handle<Object> Runtime_GetScriptFromScriptName(
10053 Handle<String> script_name) {
10054 // Scan the heap for Script objects to find the script with the requested
10055 // script data.
10056 Handle<Script> script;
10057 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010058 HeapObject* obj = NULL;
10059 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010060 // If a script is found check if it has the script data requested.
10061 if (obj->IsScript()) {
10062 if (Script::cast(obj)->name()->IsString()) {
10063 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
10064 script = Handle<Script>(Script::cast(obj));
10065 }
10066 }
10067 }
10068 }
10069
10070 // If no script with the requested script data is found return undefined.
10071 if (script.is_null()) return Factory::undefined_value();
10072
10073 // Return the script found.
10074 return GetScriptWrapper(script);
10075}
10076
10077
10078// Get the script object from script data. NOTE: Regarding performance
10079// see the NOTE for GetScriptFromScriptData.
10080// args[0]: script data for the script to find the source for
10081static Object* Runtime_GetScript(Arguments args) {
10082 HandleScope scope;
10083
10084 ASSERT(args.length() == 1);
10085
10086 CONVERT_CHECKED(String, script_name, args[0]);
10087
10088 // Find the requested script.
10089 Handle<Object> result =
10090 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
10091 return *result;
10092}
10093
10094
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010095// Determines whether the given stack frame should be displayed in
10096// a stack trace. The caller is the error constructor that asked
10097// for the stack trace to be collected. The first time a construct
10098// call to this function is encountered it is skipped. The seen_caller
10099// in/out parameter is used to remember if the caller has been seen
10100// yet.
10101static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
10102 bool* seen_caller) {
10103 // Only display JS frames.
10104 if (!raw_frame->is_java_script())
10105 return false;
10106 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
10107 Object* raw_fun = frame->function();
10108 // Not sure when this can happen but skip it just in case.
10109 if (!raw_fun->IsJSFunction())
10110 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010111 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010112 *seen_caller = true;
10113 return false;
10114 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010115 // Skip all frames until we've seen the caller. Also, skip the most
10116 // obvious builtin calls. Some builtin calls (such as Number.ADD
10117 // which is invoked using 'call') are very difficult to recognize
10118 // so we're leaving them in for now.
10119 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010120}
10121
10122
10123// Collect the raw data for a stack trace. Returns an array of three
10124// element segments each containing a receiver, function and native
10125// code offset.
10126static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010127 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010128 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010129 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
10130
10131 HandleScope scope;
10132
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +000010133 limit = Max(limit, 0); // Ensure that limit is not negative.
10134 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010135 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010136
10137 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010138 // If the caller parameter is a function we skip frames until we're
10139 // under it before starting to collect.
10140 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010141 int cursor = 0;
10142 int frames_seen = 0;
10143 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010144 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010145 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010146 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010147 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010148 Object* recv = frame->receiver();
10149 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010150 Address pc = frame->pc();
10151 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010152 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010153 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010154 if (cursor + 2 < elements->length()) {
10155 elements->set(cursor++, recv);
10156 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010157 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010158 } else {
10159 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000010160 Handle<Object> recv_handle(recv);
10161 Handle<Object> fun_handle(fun);
10162 SetElement(result, cursor++, recv_handle);
10163 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010164 SetElement(result, cursor++, Handle<Smi>(offset));
10165 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010166 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010167 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010168 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000010169
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010170 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000010171 return *result;
10172}
10173
10174
ager@chromium.org3811b432009-10-28 14:53:37 +000010175// Returns V8 version as a string.
10176static Object* Runtime_GetV8Version(Arguments args) {
10177 ASSERT_EQ(args.length(), 0);
10178
10179 NoHandleAllocation ha;
10180
10181 const char* version_string = v8::V8::GetVersion();
10182
10183 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10184}
10185
10186
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010187static Object* Runtime_Abort(Arguments args) {
10188 ASSERT(args.length() == 2);
10189 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10190 Smi::cast(args[1])->value());
10191 Top::PrintStack();
10192 OS::Abort();
10193 UNREACHABLE();
10194 return NULL;
10195}
10196
10197
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010198static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10199 ASSERT(args.length() == 0);
10200 HandleScope::DeleteExtensions();
10201 return Heap::undefined_value();
10202}
10203
10204
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010205static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10206 ASSERT(index % 2 == 0); // index of the key
10207 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10208 ASSERT(index < cache_obj->length());
10209
10210 HandleScope scope;
10211
10212 Handle<FixedArray> cache(cache_obj);
10213 Handle<Object> key(key_obj);
10214 Handle<JSFunction> factory(JSFunction::cast(
10215 cache->get(JSFunctionResultCache::kFactoryIndex)));
10216 // TODO(antonm): consider passing a receiver when constructing a cache.
10217 Handle<Object> receiver(Top::global_context()->global());
10218
10219 Handle<Object> value;
10220 {
10221 // This handle is nor shared, nor used later, so it's safe.
10222 Object** argv[] = { key.location() };
10223 bool pending_exception = false;
10224 value = Execution::Call(factory,
10225 receiver,
10226 1,
10227 argv,
10228 &pending_exception);
10229 if (pending_exception) return Failure::Exception();
10230 }
10231
10232 cache->set(index, *key);
10233 cache->set(index + 1, *value);
10234 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10235
10236 return *value;
10237}
10238
10239
10240static Object* Runtime_GetFromCache(Arguments args) {
10241 // This is only called from codegen, so checks might be more lax.
10242 CONVERT_CHECKED(FixedArray, cache, args[0]);
10243 Object* key = args[1];
10244
10245 const int finger_index =
10246 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10247
10248 Object* o = cache->get(finger_index);
10249 if (o == key) {
10250 // The fastest case: hit the same place again.
10251 return cache->get(finger_index + 1);
10252 }
10253
10254 for (int i = finger_index - 2;
10255 i >= JSFunctionResultCache::kEntriesIndex;
10256 i -= 2) {
10257 o = cache->get(i);
10258 if (o == key) {
10259 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10260 return cache->get(i + 1);
10261 }
10262 }
10263
10264 const int size =
10265 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10266 ASSERT(size <= cache->length());
10267
10268 for (int i = size - 2; i > finger_index; i -= 2) {
10269 o = cache->get(i);
10270 if (o == key) {
10271 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10272 return cache->get(i + 1);
10273 }
10274 }
10275
10276 // Cache miss. If we have spare room, put new data into it, otherwise
10277 // evict post finger entry which must be least recently used.
10278 if (size < cache->length()) {
10279 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10280 return CacheMiss(cache, size, key);
10281 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010282 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10283 if (target_index == cache->length()) {
10284 target_index = JSFunctionResultCache::kEntriesIndex;
10285 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010286 return CacheMiss(cache, target_index, key);
10287 }
10288}
10289
kasper.lund44510672008-07-25 07:37:58 +000010290#ifdef DEBUG
10291// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10292// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010293static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010294 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010295 HandleScope scope;
10296 Handle<JSArray> result = Factory::NewJSArray(0);
10297 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010298 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010299#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010300 { \
10301 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010302 Handle<String> name; \
10303 /* Inline runtime functions have an underscore in front of the name. */ \
10304 if (inline_runtime_functions) { \
10305 name = Factory::NewStringFromAscii( \
10306 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10307 } else { \
10308 name = Factory::NewStringFromAscii( \
10309 Vector<const char>(#Name, StrLength(#Name))); \
10310 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010311 Handle<JSArray> pair = Factory::NewJSArray(0); \
10312 SetElement(pair, 0, name); \
10313 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10314 SetElement(result, index++, pair); \
10315 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010316 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010317 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010318 inline_runtime_functions = true;
10319 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010320#undef ADD_ENTRY
10321 return *result;
10322}
kasper.lund44510672008-07-25 07:37:58 +000010323#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010324
10325
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010326static Object* Runtime_Log(Arguments args) {
10327 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010328 CONVERT_CHECKED(String, format, args[0]);
10329 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010330 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010331 Logger::LogRuntime(chars, elms);
10332 return Heap::undefined_value();
10333}
10334
10335
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010336static Object* Runtime_IS_VAR(Arguments args) {
10337 UNREACHABLE(); // implemented as macro in the parser
10338 return NULL;
10339}
10340
10341
10342// ----------------------------------------------------------------------------
10343// Implementation of Runtime
10344
ager@chromium.orga1645e22009-09-09 19:27:10 +000010345#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010346 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010347 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010348
10349static Runtime::Function Runtime_functions[] = {
10350 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010351 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010352};
10353
10354#undef F
10355
10356
10357Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10358 ASSERT(0 <= fid && fid < kNofFunctions);
10359 return &Runtime_functions[fid];
10360}
10361
10362
10363Runtime::Function* Runtime::FunctionForName(const char* name) {
10364 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10365 if (strcmp(f->name, name) == 0) {
10366 return f;
10367 }
10368 }
10369 return NULL;
10370}
10371
10372
10373void Runtime::PerformGC(Object* result) {
10374 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010375 if (failure->IsRetryAfterGC()) {
10376 // Try to do a garbage collection; ignore it if it fails. The C
10377 // entry stub will throw an out-of-memory exception in that case.
10378 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10379 } else {
10380 // Handle last resort GC and make sure to allow future allocations
10381 // to grow the heap without causing GCs (if possible).
10382 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010383 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010384 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010385}
10386
10387
10388} } // namespace v8::internal