blob: 3d112b0e962745d8360568415901aeb52f30e8ac [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 } else if (Array::IndexFromObject(*key, &element_index)) {
295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
572// Returns an array with the property description:
573// if args[1] is not a property on args[0]
574// returns undefined
575// if args[1] is a data property on args[0]
576// [false, value, Writeable, Enumerable, Configurable]
577// if args[1] is an accessor on args[0]
578// [true, GetFunction, SetFunction, Enumerable, Configurable]
579static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000580 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000581 HandleScope scope;
582 Handle<FixedArray> elms = Factory::NewFixedArray(5);
583 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
584 LookupResult result;
585 CONVERT_CHECKED(JSObject, obj, args[0]);
586 CONVERT_CHECKED(String, name, args[1]);
587
588 // Use recursive implementation to also traverse hidden prototypes
589 GetOwnPropertyImplementation(obj, name, &result);
590
591 if (!result.IsProperty())
592 return Heap::undefined_value();
593
594 if (result.type() == CALLBACKS) {
595 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000596 if (structure->IsProxy() || structure->IsAccessorInfo()) {
597 // Property that is internally implemented as a callback or
598 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000599 Object* value = obj->GetPropertyWithCallback(
600 obj, structure, name, result.holder());
601 elms->set(0, Heap::false_value());
602 elms->set(1, value);
603 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
604 } else if (structure->IsFixedArray()) {
605 // __defineGetter__/__defineSetter__ callback.
606 elms->set(0, Heap::true_value());
607 elms->set(1, FixedArray::cast(structure)->get(0));
608 elms->set(2, FixedArray::cast(structure)->get(1));
609 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000610 return Heap::undefined_value();
611 }
612 } else {
613 elms->set(0, Heap::false_value());
614 elms->set(1, result.GetLazyValue());
615 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
616 }
617
618 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000619 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000620 return *desc;
621}
622
623
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000624static Object* Runtime_IsExtensible(Arguments args) {
625 ASSERT(args.length() == 1);
626 CONVERT_CHECKED(JSObject, obj, args[0]);
627 return obj->map()->is_extensible() ? Heap::true_value()
628 : Heap::false_value();
629}
630
631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000632static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000633 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000635 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
636 CONVERT_ARG_CHECKED(String, pattern, 1);
637 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000638 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
639 if (result.is_null()) return Failure::Exception();
640 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641}
642
643
644static Object* Runtime_CreateApiFunction(Arguments args) {
645 HandleScope scope;
646 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000647 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000648 return *Factory::CreateApiFunction(data);
649}
650
651
652static Object* Runtime_IsTemplate(Arguments args) {
653 ASSERT(args.length() == 1);
654 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000655 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 return Heap::ToBoolean(result);
657}
658
659
660static Object* Runtime_GetTemplateField(Arguments args) {
661 ASSERT(args.length() == 2);
662 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000664 int index = field->value();
665 int offset = index * kPointerSize + HeapObject::kHeaderSize;
666 InstanceType type = templ->map()->instance_type();
667 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
668 type == OBJECT_TEMPLATE_INFO_TYPE);
669 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000670 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000671 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
672 } else {
673 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
674 }
675 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676}
677
678
ager@chromium.org870a0b62008-11-04 11:43:05 +0000679static Object* Runtime_DisableAccessChecks(Arguments args) {
680 ASSERT(args.length() == 1);
681 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000682 Map* old_map = object->map();
683 bool needs_access_checks = old_map->is_access_check_needed();
684 if (needs_access_checks) {
685 // Copy map so it won't interfere constructor's initial map.
686 Object* new_map = old_map->CopyDropTransitions();
687 if (new_map->IsFailure()) return new_map;
688
689 Map::cast(new_map)->set_is_access_check_needed(false);
690 object->set_map(Map::cast(new_map));
691 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000692 return needs_access_checks ? Heap::true_value() : Heap::false_value();
693}
694
695
696static Object* Runtime_EnableAccessChecks(Arguments args) {
697 ASSERT(args.length() == 1);
698 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000699 Map* old_map = object->map();
700 if (!old_map->is_access_check_needed()) {
701 // Copy map so it won't interfere constructor's initial map.
702 Object* new_map = old_map->CopyDropTransitions();
703 if (new_map->IsFailure()) return new_map;
704
705 Map::cast(new_map)->set_is_access_check_needed(true);
706 object->set_map(Map::cast(new_map));
707 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000708 return Heap::undefined_value();
709}
710
711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000712static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
713 HandleScope scope;
714 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
715 Handle<Object> args[2] = { type_handle, name };
716 Handle<Object> error =
717 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
718 return Top::Throw(*error);
719}
720
721
722static Object* Runtime_DeclareGlobals(Arguments args) {
723 HandleScope scope;
724 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
725
ager@chromium.org3811b432009-10-28 14:53:37 +0000726 Handle<Context> context = args.at<Context>(0);
727 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 bool is_eval = Smi::cast(args[2])->value() == 1;
729
730 // Compute the property attributes. According to ECMA-262, section
731 // 13, page 71, the property must be read-only and
732 // non-deletable. However, neither SpiderMonkey nor KJS creates the
733 // property as read-only, so we don't either.
734 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Traverse the name/value pairs and set the properties.
737 int length = pairs->length();
738 for (int i = 0; i < length; i += 2) {
739 HandleScope scope;
740 Handle<String> name(String::cast(pairs->get(i)));
741 Handle<Object> value(pairs->get(i + 1));
742
743 // We have to declare a global const property. To capture we only
744 // assign to it when evaluating the assignment for "const x =
745 // <expr>" the initial value is the hole.
746 bool is_const_property = value->IsTheHole();
747
748 if (value->IsUndefined() || is_const_property) {
749 // Lookup the property in the global object, and don't set the
750 // value of the variable if the property is already there.
751 LookupResult lookup;
752 global->Lookup(*name, &lookup);
753 if (lookup.IsProperty()) {
754 // Determine if the property is local by comparing the holder
755 // against the global object. The information will be used to
756 // avoid throwing re-declaration errors when declaring
757 // variables or constants that exist in the prototype chain.
758 bool is_local = (*global == lookup.holder());
759 // Get the property attributes and determine if the property is
760 // read-only.
761 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
762 bool is_read_only = (attributes & READ_ONLY) != 0;
763 if (lookup.type() == INTERCEPTOR) {
764 // If the interceptor says the property is there, we
765 // just return undefined without overwriting the property.
766 // Otherwise, we continue to setting the property.
767 if (attributes != ABSENT) {
768 // Check if the existing property conflicts with regards to const.
769 if (is_local && (is_read_only || is_const_property)) {
770 const char* type = (is_read_only) ? "const" : "var";
771 return ThrowRedeclarationError(type, name);
772 };
773 // The property already exists without conflicting: Go to
774 // the next declaration.
775 continue;
776 }
777 // Fall-through and introduce the absent property by using
778 // SetProperty.
779 } else {
780 if (is_local && (is_read_only || is_const_property)) {
781 const char* type = (is_read_only) ? "const" : "var";
782 return ThrowRedeclarationError(type, name);
783 }
784 // The property already exists without conflicting: Go to
785 // the next declaration.
786 continue;
787 }
788 }
789 } else {
790 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000791 Handle<SharedFunctionInfo> shared =
792 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000794 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795 value = function;
796 }
797
798 LookupResult lookup;
799 global->LocalLookup(*name, &lookup);
800
801 PropertyAttributes attributes = is_const_property
802 ? static_cast<PropertyAttributes>(base | READ_ONLY)
803 : base;
804
805 if (lookup.IsProperty()) {
806 // There's a local property that we need to overwrite because
807 // we're either declaring a function or there's an interceptor
808 // that claims the property is absent.
809
810 // Check for conflicting re-declarations. We cannot have
811 // conflicting types in case of intercepted properties because
812 // they are absent.
813 if (lookup.type() != INTERCEPTOR &&
814 (lookup.IsReadOnly() || is_const_property)) {
815 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
816 return ThrowRedeclarationError(type, name);
817 }
818 SetProperty(global, name, value, attributes);
819 } else {
820 // If a property with this name does not already exist on the
821 // global object add the property locally. We take special
822 // precautions to always add it as a local property even in case
823 // of callbacks in the prototype chain (this rules out using
824 // SetProperty). Also, we must use the handle-based version to
825 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000826 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 }
828 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 return Heap::undefined_value();
831}
832
833
834static Object* Runtime_DeclareContextSlot(Arguments args) {
835 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000836 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 CONVERT_ARG_CHECKED(Context, context, 0);
839 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000841 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000843 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000844
845 // Declarations are always done in the function context.
846 context = Handle<Context>(context->fcontext());
847
848 int index;
849 PropertyAttributes attributes;
850 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000851 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 context->Lookup(name, flags, &index, &attributes);
853
854 if (attributes != ABSENT) {
855 // The name was declared before; check for conflicting
856 // re-declarations: This is similar to the code in parser.cc in
857 // the AstBuildingParser::Declare function.
858 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
859 // Functions are not read-only.
860 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
861 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
862 return ThrowRedeclarationError(type, name);
863 }
864
865 // Initialize it if necessary.
866 if (*initial_value != NULL) {
867 if (index >= 0) {
868 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000869 // the function context or the arguments object.
870 if (holder->IsContext()) {
871 ASSERT(holder.is_identical_to(context));
872 if (((attributes & READ_ONLY) == 0) ||
873 context->get(index)->IsTheHole()) {
874 context->set(index, *initial_value);
875 }
876 } else {
877 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 }
879 } else {
880 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000881 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 SetProperty(context_ext, name, initial_value, mode);
883 }
884 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000886 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000887 // The property is not in the function context. It needs to be
888 // "declared" in the function context's extension context, or in the
889 // global context.
890 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000891 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892 // The function context's extension context exists - use it.
893 context_ext = Handle<JSObject>(context->extension());
894 } else {
895 // The function context's extension context does not exists - allocate
896 // it.
897 context_ext = Factory::NewJSObject(Top::context_extension_function());
898 // And store it in the extension slot.
899 context->set_extension(*context_ext);
900 }
901 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902
ager@chromium.org7c537e22008-10-16 08:43:32 +0000903 // Declare the property by setting it to the initial value if provided,
904 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
905 // constant declarations).
906 ASSERT(!context_ext->HasLocalProperty(*name));
907 Handle<Object> value(Heap::undefined_value());
908 if (*initial_value != NULL) value = initial_value;
909 SetProperty(context_ext, name, value, mode);
910 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
911 }
912
913 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914}
915
916
917static Object* Runtime_InitializeVarGlobal(Arguments args) {
918 NoHandleAllocation nha;
919
920 // Determine if we need to assign to the variable if it already
921 // exists (based on the number of arguments).
922 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
923 bool assign = args.length() == 2;
924
925 CONVERT_ARG_CHECKED(String, name, 0);
926 GlobalObject* global = Top::context()->global();
927
928 // According to ECMA-262, section 12.2, page 62, the property must
929 // not be deletable.
930 PropertyAttributes attributes = DONT_DELETE;
931
932 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000933 // there, there is a property with this name in the prototype chain.
934 // We follow Safari and Firefox behavior and only set the property
935 // locally if there is an explicit initialization value that we have
936 // to assign to the property. When adding the property we take
937 // special precautions to always add it as a local property even in
938 // case of callbacks in the prototype chain (this rules out using
939 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
940 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000941 // Note that objects can have hidden prototypes, so we need to traverse
942 // the whole chain of hidden prototypes to do a 'local' lookup.
943 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000945 while (true) {
946 real_holder->LocalLookup(*name, &lookup);
947 if (lookup.IsProperty()) {
948 // Determine if this is a redeclaration of something read-only.
949 if (lookup.IsReadOnly()) {
950 // If we found readonly property on one of hidden prototypes,
951 // just shadow it.
952 if (real_holder != Top::context()->global()) break;
953 return ThrowRedeclarationError("const", name);
954 }
955
956 // Determine if this is a redeclaration of an intercepted read-only
957 // property and figure out if the property exists at all.
958 bool found = true;
959 PropertyType type = lookup.type();
960 if (type == INTERCEPTOR) {
961 HandleScope handle_scope;
962 Handle<JSObject> holder(real_holder);
963 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
964 real_holder = *holder;
965 if (intercepted == ABSENT) {
966 // The interceptor claims the property isn't there. We need to
967 // make sure to introduce it.
968 found = false;
969 } else if ((intercepted & READ_ONLY) != 0) {
970 // The property is present, but read-only. Since we're trying to
971 // overwrite it with a variable declaration we must throw a
972 // re-declaration error. However if we found readonly property
973 // on one of hidden prototypes, just shadow it.
974 if (real_holder != Top::context()->global()) break;
975 return ThrowRedeclarationError("const", name);
976 }
977 }
978
979 if (found && !assign) {
980 // The global property is there and we're not assigning any value
981 // to it. Just return.
982 return Heap::undefined_value();
983 }
984
985 // Assign the value (or undefined) to the property.
986 Object* value = (assign) ? args[1] : Heap::undefined_value();
987 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000988 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000989
990 Object* proto = real_holder->GetPrototype();
991 if (!proto->IsJSObject())
992 break;
993
994 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
995 break;
996
997 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998 }
999
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001000 global = Top::context()->global();
1001 if (assign) {
1002 return global->IgnoreAttributesAndSetLocalProperty(*name,
1003 args[1],
1004 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007}
1008
1009
1010static Object* Runtime_InitializeConstGlobal(Arguments args) {
1011 // All constants are declared with an initial value. The name
1012 // of the constant is the first argument and the initial value
1013 // is the second.
1014 RUNTIME_ASSERT(args.length() == 2);
1015 CONVERT_ARG_CHECKED(String, name, 0);
1016 Handle<Object> value = args.at<Object>(1);
1017
1018 // Get the current global object from top.
1019 GlobalObject* global = Top::context()->global();
1020
1021 // According to ECMA-262, section 12.2, page 62, the property must
1022 // not be deletable. Since it's a const, it must be READ_ONLY too.
1023 PropertyAttributes attributes =
1024 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1025
1026 // Lookup the property locally in the global object. If it isn't
1027 // there, we add the property and take special precautions to always
1028 // add it as a local property even in case of callbacks in the
1029 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031 LookupResult lookup;
1032 global->LocalLookup(*name, &lookup);
1033 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001034 return global->IgnoreAttributesAndSetLocalProperty(*name,
1035 *value,
1036 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 }
1038
1039 // Determine if this is a redeclaration of something not
1040 // read-only. In case the result is hidden behind an interceptor we
1041 // need to ask it for the property attributes.
1042 if (!lookup.IsReadOnly()) {
1043 if (lookup.type() != INTERCEPTOR) {
1044 return ThrowRedeclarationError("var", name);
1045 }
1046
1047 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1048
1049 // Throw re-declaration error if the intercepted property is present
1050 // but not read-only.
1051 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1052 return ThrowRedeclarationError("var", name);
1053 }
1054
1055 // Restore global object from context (in case of GC) and continue
1056 // with setting the value because the property is either absent or
1057 // read-only. We also have to do redo the lookup.
1058 global = Top::context()->global();
1059
1060 // BUG 1213579: Handle the case where we have to set a read-only
1061 // property through an interceptor and only do it if it's
1062 // uninitialized, e.g. the hole. Nirk...
1063 global->SetProperty(*name, *value, attributes);
1064 return *value;
1065 }
1066
1067 // Set the value, but only we're assigning the initial value to a
1068 // constant. For now, we determine this by checking if the
1069 // current value is the hole.
1070 PropertyType type = lookup.type();
1071 if (type == FIELD) {
1072 FixedArray* properties = global->properties();
1073 int index = lookup.GetFieldIndex();
1074 if (properties->get(index)->IsTheHole()) {
1075 properties->set(index, *value);
1076 }
1077 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001078 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1079 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
1081 } else {
1082 // Ignore re-initialization of constants that have already been
1083 // assigned a function value.
1084 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1085 }
1086
1087 // Use the set value as the result of the operation.
1088 return *value;
1089}
1090
1091
1092static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1093 HandleScope scope;
1094 ASSERT(args.length() == 3);
1095
1096 Handle<Object> value(args[0]);
1097 ASSERT(!value->IsTheHole());
1098 CONVERT_ARG_CHECKED(Context, context, 1);
1099 Handle<String> name(String::cast(args[2]));
1100
1101 // Initializations are always done in the function context.
1102 context = Handle<Context>(context->fcontext());
1103
1104 int index;
1105 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001106 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001107 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001108 context->Lookup(name, flags, &index, &attributes);
1109
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001110 // In most situations, the property introduced by the const
1111 // declaration should be present in the context extension object.
1112 // However, because declaration and initialization are separate, the
1113 // property might have been deleted (if it was introduced by eval)
1114 // before we reach the initialization point.
1115 //
1116 // Example:
1117 //
1118 // function f() { eval("delete x; const x;"); }
1119 //
1120 // In that case, the initialization behaves like a normal assignment
1121 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001123 // Property was found in a context.
1124 if (holder->IsContext()) {
1125 // The holder cannot be the function context. If it is, there
1126 // should have been a const redeclaration error when declaring
1127 // the const property.
1128 ASSERT(!holder.is_identical_to(context));
1129 if ((attributes & READ_ONLY) == 0) {
1130 Handle<Context>::cast(holder)->set(index, *value);
1131 }
1132 } else {
1133 // The holder is an arguments object.
1134 ASSERT((attributes & READ_ONLY) == 0);
1135 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137 return *value;
1138 }
1139
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001140 // The property could not be found, we introduce it in the global
1141 // context.
1142 if (attributes == ABSENT) {
1143 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1144 SetProperty(global, name, value, NONE);
1145 return *value;
1146 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001147
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001148 // The property was present in a context extension object.
1149 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001151 if (*context_ext == context->extension()) {
1152 // This is the property that was introduced by the const
1153 // declaration. Set it if it hasn't been set before. NOTE: We
1154 // cannot use GetProperty() to get the current value as it
1155 // 'unholes' the value.
1156 LookupResult lookup;
1157 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1158 ASSERT(lookup.IsProperty()); // the property was declared
1159 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1160
1161 PropertyType type = lookup.type();
1162 if (type == FIELD) {
1163 FixedArray* properties = context_ext->properties();
1164 int index = lookup.GetFieldIndex();
1165 if (properties->get(index)->IsTheHole()) {
1166 properties->set(index, *value);
1167 }
1168 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001169 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1170 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001171 }
1172 } else {
1173 // We should not reach here. Any real, named property should be
1174 // either a field or a dictionary slot.
1175 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176 }
1177 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001178 // The property was found in a different context extension object.
1179 // Set it if it is not a read-only property.
1180 if ((attributes & READ_ONLY) == 0) {
1181 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1182 // Setting a property might throw an exception. Exceptions
1183 // are converted to empty handles in handle operations. We
1184 // need to convert back to exceptions here.
1185 if (set.is_null()) {
1186 ASSERT(Top::has_pending_exception());
1187 return Failure::Exception();
1188 }
1189 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 return *value;
1193}
1194
1195
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001196static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1197 Arguments args) {
1198 HandleScope scope;
1199 ASSERT(args.length() == 2);
1200 CONVERT_ARG_CHECKED(JSObject, object, 0);
1201 CONVERT_SMI_CHECKED(properties, args[1]);
1202 if (object->HasFastProperties()) {
1203 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1204 }
1205 return *object;
1206}
1207
1208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209static Object* Runtime_RegExpExec(Arguments args) {
1210 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001211 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001212 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1213 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001214 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001215 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001216 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001217 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001218 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001219 RUNTIME_ASSERT(index >= 0);
1220 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001221 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001222 Handle<Object> result = RegExpImpl::Exec(regexp,
1223 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001224 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001225 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001226 if (result.is_null()) return Failure::Exception();
1227 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228}
1229
1230
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001231static Object* Runtime_RegExpConstructResult(Arguments args) {
1232 ASSERT(args.length() == 3);
1233 CONVERT_SMI_CHECKED(elements_count, args[0]);
1234 if (elements_count > JSArray::kMaxFastElementsLength) {
1235 return Top::ThrowIllegalOperation();
1236 }
1237 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1238 if (new_object->IsFailure()) return new_object;
1239 FixedArray* elements = FixedArray::cast(new_object);
1240 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1241 NEW_SPACE,
1242 OLD_POINTER_SPACE);
1243 if (new_object->IsFailure()) return new_object;
1244 {
1245 AssertNoAllocation no_gc;
1246 HandleScope scope;
1247 reinterpret_cast<HeapObject*>(new_object)->
1248 set_map(Top::global_context()->regexp_result_map());
1249 }
1250 JSArray* array = JSArray::cast(new_object);
1251 array->set_properties(Heap::empty_fixed_array());
1252 array->set_elements(elements);
1253 array->set_length(Smi::FromInt(elements_count));
1254 // Write in-object properties after the length of the array.
1255 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1256 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1257 return array;
1258}
1259
1260
lrn@chromium.org25156de2010-04-06 13:10:27 +00001261static Object* Runtime_RegExpInitializeObject(Arguments args) {
1262 AssertNoAllocation no_alloc;
1263 ASSERT(args.length() == 5);
1264 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1265 CONVERT_CHECKED(String, source, args[1]);
1266
1267 Object* global = args[2];
1268 if (!global->IsTrue()) global = Heap::false_value();
1269
1270 Object* ignoreCase = args[3];
1271 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1272
1273 Object* multiline = args[4];
1274 if (!multiline->IsTrue()) multiline = Heap::false_value();
1275
1276 Map* map = regexp->map();
1277 Object* constructor = map->constructor();
1278 if (constructor->IsJSFunction() &&
1279 JSFunction::cast(constructor)->initial_map() == map) {
1280 // If we still have the original map, set in-object properties directly.
1281 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1282 // TODO(lrn): Consider skipping write barrier on booleans as well.
1283 // Both true and false should be in oldspace at all times.
1284 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1285 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1286 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1287 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1288 Smi::FromInt(0),
1289 SKIP_WRITE_BARRIER);
1290 return regexp;
1291 }
1292
1293 // Map has changed, so use generic, but slower, method.
1294 PropertyAttributes final =
1295 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1296 PropertyAttributes writable =
1297 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1298 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1299 source,
1300 final);
1301 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1302 global,
1303 final);
1304 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1305 ignoreCase,
1306 final);
1307 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1308 multiline,
1309 final);
1310 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1311 Smi::FromInt(0),
1312 writable);
1313 return regexp;
1314}
1315
1316
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001317static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1318 HandleScope scope;
1319 ASSERT(args.length() == 1);
1320 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1321 // This is necessary to enable fast checks for absence of elements
1322 // on Array.prototype and below.
1323 prototype->set_elements(Heap::empty_fixed_array());
1324 return Smi::FromInt(0);
1325}
1326
1327
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001328static void SetCustomCallGenerator(Handle<JSFunction> function,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001329 ExternalReference* generator) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001330 if (function->shared()->function_data()->IsUndefined()) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001331 function->shared()->set_function_data(*FromCData(generator->address()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001332 }
1333}
1334
1335
1336static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1337 const char* name,
1338 Builtins::Name builtin_name,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001339 ExternalReference* generator = NULL) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001340 Handle<String> key = Factory::LookupAsciiSymbol(name);
1341 Handle<Code> code(Builtins::builtin(builtin_name));
1342 Handle<JSFunction> optimized = Factory::NewFunction(key,
1343 JS_OBJECT_TYPE,
1344 JSObject::kHeaderSize,
1345 code,
1346 false);
1347 optimized->shared()->DontAdaptArguments();
1348 if (generator != NULL) {
1349 SetCustomCallGenerator(optimized, generator);
1350 }
1351 SetProperty(holder, key, optimized, NONE);
1352 return optimized;
1353}
1354
1355
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001356Object* CompileArrayPushCall(CallStubCompiler* compiler,
1357 Object* object,
1358 JSObject* holder,
1359 JSFunction* function,
1360 String* name,
1361 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001362 return compiler->CompileArrayPushCall(object, holder, function, name, check);
1363}
1364
1365
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001366Object* CompileArrayPopCall(CallStubCompiler* compiler,
1367 Object* object,
1368 JSObject* holder,
1369 JSFunction* function,
1370 String* name,
1371 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001372 return compiler->CompileArrayPopCall(object, holder, function, name, check);
1373}
1374
1375
1376static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1377 HandleScope scope;
1378 ASSERT(args.length() == 1);
1379 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1380
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001381 ExternalReference pop = ExternalReference::compile_array_pop_call();
1382 ExternalReference push = ExternalReference::compile_array_push_call();
1383
1384 InstallBuiltin(holder, "pop", Builtins::ArrayPop, &pop);
1385 InstallBuiltin(holder, "push", Builtins::ArrayPush, &push);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001386 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1387 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1388 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1389 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001390 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001391
1392 return *holder;
1393}
1394
1395
ager@chromium.org357bf652010-04-12 11:30:10 +00001396static Object* Runtime_GetGlobalReceiver(Arguments args) {
1397 // Returns a real global receiver, not one of builtins object.
1398 Context* global_context = Top::context()->global()->global_context();
1399 return global_context->global()->global_receiver();
1400}
1401
1402
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001403static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1404 HandleScope scope;
1405 ASSERT(args.length() == 4);
1406 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1407 int index = Smi::cast(args[1])->value();
1408 Handle<String> pattern = args.at<String>(2);
1409 Handle<String> flags = args.at<String>(3);
1410
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001411 // Get the RegExp function from the context in the literals array.
1412 // This is the RegExp function from the context in which the
1413 // function was created. We do not use the RegExp function from the
1414 // current global context because this might be the RegExp function
1415 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001416 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001417 Handle<JSFunction>(
1418 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001419 // Compute the regular expression literal.
1420 bool has_pending_exception;
1421 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001422 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1423 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001424 if (has_pending_exception) {
1425 ASSERT(Top::has_pending_exception());
1426 return Failure::Exception();
1427 }
1428 literals->set(index, *regexp);
1429 return *regexp;
1430}
1431
1432
1433static Object* Runtime_FunctionGetName(Arguments args) {
1434 NoHandleAllocation ha;
1435 ASSERT(args.length() == 1);
1436
1437 CONVERT_CHECKED(JSFunction, f, args[0]);
1438 return f->shared()->name();
1439}
1440
1441
ager@chromium.org236ad962008-09-25 09:45:57 +00001442static Object* Runtime_FunctionSetName(Arguments args) {
1443 NoHandleAllocation ha;
1444 ASSERT(args.length() == 2);
1445
1446 CONVERT_CHECKED(JSFunction, f, args[0]);
1447 CONVERT_CHECKED(String, name, args[1]);
1448 f->shared()->set_name(name);
1449 return Heap::undefined_value();
1450}
1451
1452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001453static Object* Runtime_FunctionGetScript(Arguments args) {
1454 HandleScope scope;
1455 ASSERT(args.length() == 1);
1456
1457 CONVERT_CHECKED(JSFunction, fun, args[0]);
1458 Handle<Object> script = Handle<Object>(fun->shared()->script());
1459 if (!script->IsScript()) return Heap::undefined_value();
1460
1461 return *GetScriptWrapper(Handle<Script>::cast(script));
1462}
1463
1464
1465static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1466 NoHandleAllocation ha;
1467 ASSERT(args.length() == 1);
1468
1469 CONVERT_CHECKED(JSFunction, f, args[0]);
1470 return f->shared()->GetSourceCode();
1471}
1472
1473
1474static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1475 NoHandleAllocation ha;
1476 ASSERT(args.length() == 1);
1477
1478 CONVERT_CHECKED(JSFunction, fun, args[0]);
1479 int pos = fun->shared()->start_position();
1480 return Smi::FromInt(pos);
1481}
1482
1483
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001484static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1485 ASSERT(args.length() == 2);
1486
1487 CONVERT_CHECKED(JSFunction, fun, args[0]);
1488 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1489
1490 Code* code = fun->code();
1491 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1492
1493 Address pc = code->address() + offset;
1494 return Smi::FromInt(fun->code()->SourcePosition(pc));
1495}
1496
1497
1498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001499static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 2);
1502
1503 CONVERT_CHECKED(JSFunction, fun, args[0]);
1504 CONVERT_CHECKED(String, name, args[1]);
1505 fun->SetInstanceClassName(name);
1506 return Heap::undefined_value();
1507}
1508
1509
1510static Object* Runtime_FunctionSetLength(Arguments args) {
1511 NoHandleAllocation ha;
1512 ASSERT(args.length() == 2);
1513
1514 CONVERT_CHECKED(JSFunction, fun, args[0]);
1515 CONVERT_CHECKED(Smi, length, args[1]);
1516 fun->shared()->set_length(length->value());
1517 return length;
1518}
1519
1520
1521static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001522 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001523 ASSERT(args.length() == 2);
1524
1525 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001526 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1527 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001528 return args[0]; // return TOS
1529}
1530
1531
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001532static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1533 NoHandleAllocation ha;
1534 ASSERT(args.length() == 1);
1535
1536 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001537 return f->shared()->IsApiFunction() ? Heap::true_value()
1538 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001539}
1540
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001541static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1542 NoHandleAllocation ha;
1543 ASSERT(args.length() == 1);
1544
1545 CONVERT_CHECKED(JSFunction, f, args[0]);
1546 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1547}
1548
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001550static Object* Runtime_SetCode(Arguments args) {
1551 HandleScope scope;
1552 ASSERT(args.length() == 2);
1553
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001554 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001555 Handle<Object> code = args.at<Object>(1);
1556
1557 Handle<Context> context(target->context());
1558
1559 if (!code->IsNull()) {
1560 RUNTIME_ASSERT(code->IsJSFunction());
1561 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001562 Handle<SharedFunctionInfo> shared(fun->shared());
1563 SetExpectedNofProperties(target, shared->expected_nof_properties());
1564
1565 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001566 return Failure::Exception();
1567 }
1568 // Set the code, formal parameter count, and the length of the target
1569 // function.
1570 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001571 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001572 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001573 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001574 // Set the source code of the target function to undefined.
1575 // SetCode is only used for built-in constructors like String,
1576 // Array, and Object, and some web code
1577 // doesn't like seeing source code for constructors.
1578 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001579 // Clear the optimization hints related to the compiled code as these are no
1580 // longer valid when the code is overwritten.
1581 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001582 context = Handle<Context>(fun->context());
1583
1584 // Make sure we get a fresh copy of the literal vector to avoid
1585 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001586 int number_of_literals = fun->NumberOfLiterals();
1587 Handle<FixedArray> literals =
1588 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001589 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001590 // Insert the object, regexp and array functions in the literals
1591 // array prefix. These are the functions that will be used when
1592 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001593 literals->set(JSFunction::kLiteralGlobalContextIndex,
1594 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001595 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001596 // It's okay to skip the write barrier here because the literals
1597 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001598 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001599 }
1600
1601 target->set_context(*context);
1602 return *target;
1603}
1604
1605
1606static Object* CharCodeAt(String* subject, Object* index) {
1607 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001608 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001609 // Flatten the string. If someone wants to get a char at an index
1610 // in a cons string, it is likely that more indices will be
1611 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001612 Object* flat = subject->TryFlatten();
1613 if (flat->IsFailure()) return flat;
1614 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001615 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001616 return Heap::nan_value();
1617 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001618 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001619}
1620
1621
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001622static Object* CharFromCode(Object* char_code) {
1623 uint32_t code;
1624 if (Array::IndexFromObject(char_code, &code)) {
1625 if (code <= 0xffff) {
1626 return Heap::LookupSingleCharacterStringFromCode(code);
1627 }
1628 }
1629 return Heap::empty_string();
1630}
1631
1632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001633static Object* Runtime_StringCharCodeAt(Arguments args) {
1634 NoHandleAllocation ha;
1635 ASSERT(args.length() == 2);
1636
1637 CONVERT_CHECKED(String, subject, args[0]);
1638 Object* index = args[1];
1639 return CharCodeAt(subject, index);
1640}
1641
1642
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001643static Object* Runtime_StringCharAt(Arguments args) {
1644 NoHandleAllocation ha;
1645 ASSERT(args.length() == 2);
1646
1647 CONVERT_CHECKED(String, subject, args[0]);
1648 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001649 Object* code = CharCodeAt(subject, index);
1650 if (code == Heap::nan_value()) {
1651 return Heap::undefined_value();
1652 }
1653 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001654}
1655
1656
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001657static Object* Runtime_CharFromCode(Arguments args) {
1658 NoHandleAllocation ha;
1659 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001660 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001661}
1662
lrn@chromium.org25156de2010-04-06 13:10:27 +00001663
1664class FixedArrayBuilder {
1665 public:
1666 explicit FixedArrayBuilder(int initial_capacity)
1667 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1668 length_(0) {
1669 // Require a non-zero initial size. Ensures that doubling the size to
1670 // extend the array will work.
1671 ASSERT(initial_capacity > 0);
1672 }
1673
1674 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1675 : array_(backing_store),
1676 length_(0) {
1677 // Require a non-zero initial size. Ensures that doubling the size to
1678 // extend the array will work.
1679 ASSERT(backing_store->length() > 0);
1680 }
1681
1682 bool HasCapacity(int elements) {
1683 int length = array_->length();
1684 int required_length = length_ + elements;
1685 return (length >= required_length);
1686 }
1687
1688 void EnsureCapacity(int elements) {
1689 int length = array_->length();
1690 int required_length = length_ + elements;
1691 if (length < required_length) {
1692 int new_length = length;
1693 do {
1694 new_length *= 2;
1695 } while (new_length < required_length);
1696 Handle<FixedArray> extended_array =
1697 Factory::NewFixedArrayWithHoles(new_length);
1698 array_->CopyTo(0, *extended_array, 0, length_);
1699 array_ = extended_array;
1700 }
1701 }
1702
1703 void Add(Object* value) {
1704 ASSERT(length_ < capacity());
1705 array_->set(length_, value);
1706 length_++;
1707 }
1708
1709 void Add(Smi* value) {
1710 ASSERT(length_ < capacity());
1711 array_->set(length_, value);
1712 length_++;
1713 }
1714
1715 Handle<FixedArray> array() {
1716 return array_;
1717 }
1718
1719 int length() {
1720 return length_;
1721 }
1722
1723 int capacity() {
1724 return array_->length();
1725 }
1726
1727 Handle<JSArray> ToJSArray() {
1728 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1729 result_array->set_length(Smi::FromInt(length_));
1730 return result_array;
1731 }
1732
1733 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1734 target_array->set_elements(*array_);
1735 target_array->set_length(Smi::FromInt(length_));
1736 return target_array;
1737 }
1738
1739 private:
1740 Handle<FixedArray> array_;
1741 int length_;
1742};
1743
1744
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001745// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001746const int kStringBuilderConcatHelperLengthBits = 11;
1747const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001748
1749template <typename schar>
1750static inline void StringBuilderConcatHelper(String*,
1751 schar*,
1752 FixedArray*,
1753 int);
1754
lrn@chromium.org25156de2010-04-06 13:10:27 +00001755typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1756 StringBuilderSubstringLength;
1757typedef BitField<int,
1758 kStringBuilderConcatHelperLengthBits,
1759 kStringBuilderConcatHelperPositionBits>
1760 StringBuilderSubstringPosition;
1761
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001762
1763class ReplacementStringBuilder {
1764 public:
1765 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001766 : array_builder_(estimated_part_count),
1767 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001768 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001769 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001770 // Require a non-zero initial size. Ensures that doubling the size to
1771 // extend the array will work.
1772 ASSERT(estimated_part_count > 0);
1773 }
1774
lrn@chromium.org25156de2010-04-06 13:10:27 +00001775 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1776 int from,
1777 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001778 ASSERT(from >= 0);
1779 int length = to - from;
1780 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001781 if (StringBuilderSubstringLength::is_valid(length) &&
1782 StringBuilderSubstringPosition::is_valid(from)) {
1783 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1784 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001785 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001786 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001787 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001788 builder->Add(Smi::FromInt(-length));
1789 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001790 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001791 }
1792
1793
1794 void EnsureCapacity(int elements) {
1795 array_builder_.EnsureCapacity(elements);
1796 }
1797
1798
1799 void AddSubjectSlice(int from, int to) {
1800 AddSubjectSlice(&array_builder_, from, to);
1801 // Can we encode the slice in 11 bits for length and 19 bits for
1802 // start position - as used by StringBuilderConcatHelper?
1803 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001804 }
1805
1806
1807 void AddString(Handle<String> string) {
1808 int length = string->length();
1809 ASSERT(length > 0);
1810 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001811 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001812 is_ascii_ = false;
1813 }
1814 IncrementCharacterCount(length);
1815 }
1816
1817
1818 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001819 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001820 return Factory::empty_string();
1821 }
1822
1823 Handle<String> joined_string;
1824 if (is_ascii_) {
1825 joined_string = NewRawAsciiString(character_count_);
1826 AssertNoAllocation no_alloc;
1827 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1828 char* char_buffer = seq->GetChars();
1829 StringBuilderConcatHelper(*subject_,
1830 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001831 *array_builder_.array(),
1832 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001833 } else {
1834 // Non-ASCII.
1835 joined_string = NewRawTwoByteString(character_count_);
1836 AssertNoAllocation no_alloc;
1837 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1838 uc16* char_buffer = seq->GetChars();
1839 StringBuilderConcatHelper(*subject_,
1840 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001841 *array_builder_.array(),
1842 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001843 }
1844 return joined_string;
1845 }
1846
1847
1848 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001849 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001850 V8::FatalProcessOutOfMemory("String.replace result too large.");
1851 }
1852 character_count_ += by;
1853 }
1854
lrn@chromium.org25156de2010-04-06 13:10:27 +00001855 Handle<JSArray> GetParts() {
1856 Handle<JSArray> result =
1857 Factory::NewJSArrayWithElements(array_builder_.array());
1858 result->set_length(Smi::FromInt(array_builder_.length()));
1859 return result;
1860 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001861
lrn@chromium.org25156de2010-04-06 13:10:27 +00001862 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001863 Handle<String> NewRawAsciiString(int size) {
1864 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1865 }
1866
1867
1868 Handle<String> NewRawTwoByteString(int size) {
1869 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1870 }
1871
1872
1873 void AddElement(Object* element) {
1874 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001875 ASSERT(array_builder_.capacity() > array_builder_.length());
1876 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001877 }
1878
lrn@chromium.org25156de2010-04-06 13:10:27 +00001879 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001880 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001881 int character_count_;
1882 bool is_ascii_;
1883};
1884
1885
1886class CompiledReplacement {
1887 public:
1888 CompiledReplacement()
1889 : parts_(1), replacement_substrings_(0) {}
1890
1891 void Compile(Handle<String> replacement,
1892 int capture_count,
1893 int subject_length);
1894
1895 void Apply(ReplacementStringBuilder* builder,
1896 int match_from,
1897 int match_to,
1898 Handle<JSArray> last_match_info);
1899
1900 // Number of distinct parts of the replacement pattern.
1901 int parts() {
1902 return parts_.length();
1903 }
1904 private:
1905 enum PartType {
1906 SUBJECT_PREFIX = 1,
1907 SUBJECT_SUFFIX,
1908 SUBJECT_CAPTURE,
1909 REPLACEMENT_SUBSTRING,
1910 REPLACEMENT_STRING,
1911
1912 NUMBER_OF_PART_TYPES
1913 };
1914
1915 struct ReplacementPart {
1916 static inline ReplacementPart SubjectMatch() {
1917 return ReplacementPart(SUBJECT_CAPTURE, 0);
1918 }
1919 static inline ReplacementPart SubjectCapture(int capture_index) {
1920 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1921 }
1922 static inline ReplacementPart SubjectPrefix() {
1923 return ReplacementPart(SUBJECT_PREFIX, 0);
1924 }
1925 static inline ReplacementPart SubjectSuffix(int subject_length) {
1926 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1927 }
1928 static inline ReplacementPart ReplacementString() {
1929 return ReplacementPart(REPLACEMENT_STRING, 0);
1930 }
1931 static inline ReplacementPart ReplacementSubString(int from, int to) {
1932 ASSERT(from >= 0);
1933 ASSERT(to > from);
1934 return ReplacementPart(-from, to);
1935 }
1936
1937 // If tag <= 0 then it is the negation of a start index of a substring of
1938 // the replacement pattern, otherwise it's a value from PartType.
1939 ReplacementPart(int tag, int data)
1940 : tag(tag), data(data) {
1941 // Must be non-positive or a PartType value.
1942 ASSERT(tag < NUMBER_OF_PART_TYPES);
1943 }
1944 // Either a value of PartType or a non-positive number that is
1945 // the negation of an index into the replacement string.
1946 int tag;
1947 // The data value's interpretation depends on the value of tag:
1948 // tag == SUBJECT_PREFIX ||
1949 // tag == SUBJECT_SUFFIX: data is unused.
1950 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1951 // tag == REPLACEMENT_SUBSTRING ||
1952 // tag == REPLACEMENT_STRING: data is index into array of substrings
1953 // of the replacement string.
1954 // tag <= 0: Temporary representation of the substring of the replacement
1955 // string ranging over -tag .. data.
1956 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1957 // substring objects.
1958 int data;
1959 };
1960
1961 template<typename Char>
1962 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1963 Vector<Char> characters,
1964 int capture_count,
1965 int subject_length) {
1966 int length = characters.length();
1967 int last = 0;
1968 for (int i = 0; i < length; i++) {
1969 Char c = characters[i];
1970 if (c == '$') {
1971 int next_index = i + 1;
1972 if (next_index == length) { // No next character!
1973 break;
1974 }
1975 Char c2 = characters[next_index];
1976 switch (c2) {
1977 case '$':
1978 if (i > last) {
1979 // There is a substring before. Include the first "$".
1980 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1981 last = next_index + 1; // Continue after the second "$".
1982 } else {
1983 // Let the next substring start with the second "$".
1984 last = next_index;
1985 }
1986 i = next_index;
1987 break;
1988 case '`':
1989 if (i > last) {
1990 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1991 }
1992 parts->Add(ReplacementPart::SubjectPrefix());
1993 i = next_index;
1994 last = i + 1;
1995 break;
1996 case '\'':
1997 if (i > last) {
1998 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1999 }
2000 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
2001 i = next_index;
2002 last = i + 1;
2003 break;
2004 case '&':
2005 if (i > last) {
2006 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2007 }
2008 parts->Add(ReplacementPart::SubjectMatch());
2009 i = next_index;
2010 last = i + 1;
2011 break;
2012 case '0':
2013 case '1':
2014 case '2':
2015 case '3':
2016 case '4':
2017 case '5':
2018 case '6':
2019 case '7':
2020 case '8':
2021 case '9': {
2022 int capture_ref = c2 - '0';
2023 if (capture_ref > capture_count) {
2024 i = next_index;
2025 continue;
2026 }
2027 int second_digit_index = next_index + 1;
2028 if (second_digit_index < length) {
2029 // Peek ahead to see if we have two digits.
2030 Char c3 = characters[second_digit_index];
2031 if ('0' <= c3 && c3 <= '9') { // Double digits.
2032 int double_digit_ref = capture_ref * 10 + c3 - '0';
2033 if (double_digit_ref <= capture_count) {
2034 next_index = second_digit_index;
2035 capture_ref = double_digit_ref;
2036 }
2037 }
2038 }
2039 if (capture_ref > 0) {
2040 if (i > last) {
2041 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2042 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002043 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002044 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2045 last = next_index + 1;
2046 }
2047 i = next_index;
2048 break;
2049 }
2050 default:
2051 i = next_index;
2052 break;
2053 }
2054 }
2055 }
2056 if (length > last) {
2057 if (last == 0) {
2058 parts->Add(ReplacementPart::ReplacementString());
2059 } else {
2060 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2061 }
2062 }
2063 }
2064
2065 ZoneList<ReplacementPart> parts_;
2066 ZoneList<Handle<String> > replacement_substrings_;
2067};
2068
2069
2070void CompiledReplacement::Compile(Handle<String> replacement,
2071 int capture_count,
2072 int subject_length) {
2073 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002074 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002075 AssertNoAllocation no_alloc;
2076 ParseReplacementPattern(&parts_,
2077 replacement->ToAsciiVector(),
2078 capture_count,
2079 subject_length);
2080 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002081 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002082 AssertNoAllocation no_alloc;
2083
2084 ParseReplacementPattern(&parts_,
2085 replacement->ToUC16Vector(),
2086 capture_count,
2087 subject_length);
2088 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002089 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002090 int substring_index = 0;
2091 for (int i = 0, n = parts_.length(); i < n; i++) {
2092 int tag = parts_[i].tag;
2093 if (tag <= 0) { // A replacement string slice.
2094 int from = -tag;
2095 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002096 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002097 parts_[i].tag = REPLACEMENT_SUBSTRING;
2098 parts_[i].data = substring_index;
2099 substring_index++;
2100 } else if (tag == REPLACEMENT_STRING) {
2101 replacement_substrings_.Add(replacement);
2102 parts_[i].data = substring_index;
2103 substring_index++;
2104 }
2105 }
2106}
2107
2108
2109void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2110 int match_from,
2111 int match_to,
2112 Handle<JSArray> last_match_info) {
2113 for (int i = 0, n = parts_.length(); i < n; i++) {
2114 ReplacementPart part = parts_[i];
2115 switch (part.tag) {
2116 case SUBJECT_PREFIX:
2117 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2118 break;
2119 case SUBJECT_SUFFIX: {
2120 int subject_length = part.data;
2121 if (match_to < subject_length) {
2122 builder->AddSubjectSlice(match_to, subject_length);
2123 }
2124 break;
2125 }
2126 case SUBJECT_CAPTURE: {
2127 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002128 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002129 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2130 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2131 if (from >= 0 && to > from) {
2132 builder->AddSubjectSlice(from, to);
2133 }
2134 break;
2135 }
2136 case REPLACEMENT_SUBSTRING:
2137 case REPLACEMENT_STRING:
2138 builder->AddString(replacement_substrings_[part.data]);
2139 break;
2140 default:
2141 UNREACHABLE();
2142 }
2143 }
2144}
2145
2146
2147
2148static Object* StringReplaceRegExpWithString(String* subject,
2149 JSRegExp* regexp,
2150 String* replacement,
2151 JSArray* last_match_info) {
2152 ASSERT(subject->IsFlat());
2153 ASSERT(replacement->IsFlat());
2154
2155 HandleScope handles;
2156
2157 int length = subject->length();
2158 Handle<String> subject_handle(subject);
2159 Handle<JSRegExp> regexp_handle(regexp);
2160 Handle<String> replacement_handle(replacement);
2161 Handle<JSArray> last_match_info_handle(last_match_info);
2162 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2163 subject_handle,
2164 0,
2165 last_match_info_handle);
2166 if (match.is_null()) {
2167 return Failure::Exception();
2168 }
2169 if (match->IsNull()) {
2170 return *subject_handle;
2171 }
2172
2173 int capture_count = regexp_handle->CaptureCount();
2174
2175 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002176 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002177 CompiledReplacement compiled_replacement;
2178 compiled_replacement.Compile(replacement_handle,
2179 capture_count,
2180 length);
2181
2182 bool is_global = regexp_handle->GetFlags().is_global();
2183
2184 // Guessing the number of parts that the final result string is built
2185 // from. Global regexps can match any number of times, so we guess
2186 // conservatively.
2187 int expected_parts =
2188 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2189 ReplacementStringBuilder builder(subject_handle, expected_parts);
2190
2191 // Index of end of last match.
2192 int prev = 0;
2193
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002194 // Number of parts added by compiled replacement plus preceeding
2195 // string and possibly suffix after last match. It is possible for
2196 // all components to use two elements when encoded as two smis.
2197 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002198 bool matched = true;
2199 do {
2200 ASSERT(last_match_info_handle->HasFastElements());
2201 // Increase the capacity of the builder before entering local handle-scope,
2202 // so its internal buffer can safely allocate a new handle if it grows.
2203 builder.EnsureCapacity(parts_added_per_loop);
2204
2205 HandleScope loop_scope;
2206 int start, end;
2207 {
2208 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002209 FixedArray* match_info_array =
2210 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002211
2212 ASSERT_EQ(capture_count * 2 + 2,
2213 RegExpImpl::GetLastCaptureCount(match_info_array));
2214 start = RegExpImpl::GetCapture(match_info_array, 0);
2215 end = RegExpImpl::GetCapture(match_info_array, 1);
2216 }
2217
2218 if (prev < start) {
2219 builder.AddSubjectSlice(prev, start);
2220 }
2221 compiled_replacement.Apply(&builder,
2222 start,
2223 end,
2224 last_match_info_handle);
2225 prev = end;
2226
2227 // Only continue checking for global regexps.
2228 if (!is_global) break;
2229
2230 // Continue from where the match ended, unless it was an empty match.
2231 int next = end;
2232 if (start == end) {
2233 next = end + 1;
2234 if (next > length) break;
2235 }
2236
2237 match = RegExpImpl::Exec(regexp_handle,
2238 subject_handle,
2239 next,
2240 last_match_info_handle);
2241 if (match.is_null()) {
2242 return Failure::Exception();
2243 }
2244 matched = !match->IsNull();
2245 } while (matched);
2246
2247 if (prev < length) {
2248 builder.AddSubjectSlice(prev, length);
2249 }
2250
2251 return *(builder.ToString());
2252}
2253
2254
2255static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2256 ASSERT(args.length() == 4);
2257
2258 CONVERT_CHECKED(String, subject, args[0]);
2259 if (!subject->IsFlat()) {
2260 Object* flat_subject = subject->TryFlatten();
2261 if (flat_subject->IsFailure()) {
2262 return flat_subject;
2263 }
2264 subject = String::cast(flat_subject);
2265 }
2266
2267 CONVERT_CHECKED(String, replacement, args[2]);
2268 if (!replacement->IsFlat()) {
2269 Object* flat_replacement = replacement->TryFlatten();
2270 if (flat_replacement->IsFailure()) {
2271 return flat_replacement;
2272 }
2273 replacement = String::cast(flat_replacement);
2274 }
2275
2276 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2277 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2278
2279 ASSERT(last_match_info->HasFastElements());
2280
2281 return StringReplaceRegExpWithString(subject,
2282 regexp,
2283 replacement,
2284 last_match_info);
2285}
2286
2287
ager@chromium.org7c537e22008-10-16 08:43:32 +00002288// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2289// limit, we can fix the size of tables.
2290static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002291// Reduce alphabet to this size.
2292static const int kBMAlphabetSize = 0x100;
2293// For patterns below this length, the skip length of Boyer-Moore is too short
2294// to compensate for the algorithmic overhead compared to simple brute force.
2295static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002296
ager@chromium.org7c537e22008-10-16 08:43:32 +00002297// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2298// shift. Only allows the last kBMMaxShift characters of the needle
2299// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002300class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002301 public:
2302 BMGoodSuffixBuffers() {}
2303 inline void init(int needle_length) {
2304 ASSERT(needle_length > 1);
2305 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2306 int len = needle_length - start;
2307 biased_suffixes_ = suffixes_ - start;
2308 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2309 for (int i = 0; i <= len; i++) {
2310 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002311 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002312 }
2313 inline int& suffix(int index) {
2314 ASSERT(biased_suffixes_ + index >= suffixes_);
2315 return biased_suffixes_[index];
2316 }
2317 inline int& shift(int index) {
2318 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2319 return biased_good_suffix_shift_[index];
2320 }
2321 private:
2322 int suffixes_[kBMMaxShift + 1];
2323 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002324 int* biased_suffixes_;
2325 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002326 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2327};
2328
2329// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002330static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002331static BMGoodSuffixBuffers bmgs_buffers;
2332
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002333// State of the string match tables.
2334// SIMPLE: No usable content in the buffers.
2335// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2336// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2337// Whenever starting with a new needle, one should call InitializeStringSearch
2338// to determine which search strategy to use, and in the case of a long-needle
2339// strategy, the call also initializes the algorithm to SIMPLE.
2340enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2341static StringSearchAlgorithm algorithm;
2342
2343
ager@chromium.org7c537e22008-10-16 08:43:32 +00002344// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002345template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002346static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2347 // Only preprocess at most kBMMaxShift last characters of pattern.
2348 int start = pattern.length() < kBMMaxShift ? 0
2349 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002350 // Run forwards to populate bad_char_table, so that *last* instance
2351 // of character equivalence class is the one registered.
2352 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002353 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2354 : kBMAlphabetSize;
2355 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002356 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002357 } else {
2358 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002359 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002360 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002361 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002362 for (int i = start; i < pattern.length() - 1; i++) {
2363 pchar c = pattern[i];
2364 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002365 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002366 }
2367}
2368
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002369
ager@chromium.org7c537e22008-10-16 08:43:32 +00002370template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002371static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002372 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002373 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002374 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002375 // Compute Good Suffix tables.
2376 bmgs_buffers.init(m);
2377
2378 bmgs_buffers.shift(m-1) = 1;
2379 bmgs_buffers.suffix(m) = m + 1;
2380 pchar last_char = pattern[m - 1];
2381 int suffix = m + 1;
2382 for (int i = m; i > start;) {
2383 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2384 if (bmgs_buffers.shift(suffix) == len) {
2385 bmgs_buffers.shift(suffix) = suffix - i;
2386 }
2387 suffix = bmgs_buffers.suffix(suffix);
2388 }
2389 i--;
2390 suffix--;
2391 bmgs_buffers.suffix(i) = suffix;
2392 if (suffix == m) {
2393 // No suffix to extend, so we check against last_char only.
2394 while (i > start && pattern[i - 1] != last_char) {
2395 if (bmgs_buffers.shift(m) == len) {
2396 bmgs_buffers.shift(m) = m - i;
2397 }
2398 i--;
2399 bmgs_buffers.suffix(i) = m;
2400 }
2401 if (i > start) {
2402 i--;
2403 suffix--;
2404 bmgs_buffers.suffix(i) = suffix;
2405 }
2406 }
2407 }
2408 if (suffix < m) {
2409 for (int i = start; i <= m; i++) {
2410 if (bmgs_buffers.shift(i) == len) {
2411 bmgs_buffers.shift(i) = suffix - start;
2412 }
2413 if (i == suffix) {
2414 suffix = bmgs_buffers.suffix(suffix);
2415 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002416 }
2417 }
2418}
2419
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002420
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002421template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002422static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002423 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002424 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002425 }
2426 if (sizeof(pchar) == 1) {
2427 if (char_code > String::kMaxAsciiCharCode) {
2428 return -1;
2429 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002430 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002431 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002432 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002433}
2434
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002435
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002436// Restricted simplified Boyer-Moore string matching.
2437// Uses only the bad-shift table of Boyer-Moore and only uses it
2438// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002439template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002440static int BoyerMooreHorspool(Vector<const schar> subject,
2441 Vector<const pchar> pattern,
2442 int start_index,
2443 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002444 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002445 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002446 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002447
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002448 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002449
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002450 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002451 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002452 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002453 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002454 // Perform search
2455 for (idx = start_index; idx <= n - m;) {
2456 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002457 int c;
2458 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002459 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002460 int shift = j - bc_occ;
2461 idx += shift;
2462 badness += 1 - shift; // at most zero, so badness cannot increase.
2463 if (idx > n - m) {
2464 *complete = true;
2465 return -1;
2466 }
2467 }
2468 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002469 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002470 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002471 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002472 return idx;
2473 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002474 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002475 // Badness increases by the number of characters we have
2476 // checked, and decreases by the number of characters we
2477 // can skip by shifting. It's a measure of how we are doing
2478 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002479 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002480 if (badness > 0) {
2481 *complete = false;
2482 return idx;
2483 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002484 }
2485 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002486 *complete = true;
2487 return -1;
2488}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002489
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002490
2491template <typename schar, typename pchar>
2492static int BoyerMooreIndexOf(Vector<const schar> subject,
2493 Vector<const pchar> pattern,
2494 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002495 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002496 int n = subject.length();
2497 int m = pattern.length();
2498 // Only preprocess at most kBMMaxShift last characters of pattern.
2499 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2500
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002501 pchar last_char = pattern[m - 1];
2502 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002503 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002504 int j = m - 1;
2505 schar c;
2506 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002507 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002508 idx += shift;
2509 if (idx > n - m) {
2510 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002511 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002512 }
2513 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2514 if (j < 0) {
2515 return idx;
2516 } else if (j < start) {
2517 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002518 // Fall back on BMH shift.
2519 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002520 } else {
2521 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002522 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002523 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002524 if (gs_shift > shift) {
2525 shift = gs_shift;
2526 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002527 idx += shift;
2528 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002529 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002530
2531 return -1;
2532}
2533
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002534
2535template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002536static inline int SingleCharIndexOf(Vector<const schar> string,
2537 schar pattern_char,
2538 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002539 if (sizeof(schar) == 1) {
2540 const schar* pos = reinterpret_cast<const schar*>(
2541 memchr(string.start() + start_index,
2542 pattern_char,
2543 string.length() - start_index));
2544 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002545 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002546 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002547 for (int i = start_index, n = string.length(); i < n; i++) {
2548 if (pattern_char == string[i]) {
2549 return i;
2550 }
2551 }
2552 return -1;
2553}
2554
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002555
2556template <typename schar>
2557static int SingleCharLastIndexOf(Vector<const schar> string,
2558 schar pattern_char,
2559 int start_index) {
2560 for (int i = start_index; i >= 0; i--) {
2561 if (pattern_char == string[i]) {
2562 return i;
2563 }
2564 }
2565 return -1;
2566}
2567
2568
ager@chromium.org7c537e22008-10-16 08:43:32 +00002569// Trivial string search for shorter strings.
2570// On return, if "complete" is set to true, the return value is the
2571// final result of searching for the patter in the subject.
2572// If "complete" is set to false, the return value is the index where
2573// further checking should start, i.e., it's guaranteed that the pattern
2574// does not occur at a position prior to the returned index.
2575template <typename pchar, typename schar>
2576static int SimpleIndexOf(Vector<const schar> subject,
2577 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002578 int idx,
2579 bool* complete) {
2580 // Badness is a count of how much work we have done. When we have
2581 // done enough work we decide it's probably worth switching to a better
2582 // algorithm.
2583 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002584
ager@chromium.org7c537e22008-10-16 08:43:32 +00002585 // We know our pattern is at least 2 characters, we cache the first so
2586 // the common case of the first character not matching is faster.
2587 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002588 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2589 badness++;
2590 if (badness > 0) {
2591 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002592 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002593 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002594 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2595 const schar* pos = reinterpret_cast<const schar*>(
2596 memchr(subject.start() + i,
2597 pattern_first_char,
2598 n - i + 1));
2599 if (pos == NULL) {
2600 *complete = true;
2601 return -1;
2602 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002603 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002604 } else {
2605 if (subject[i] != pattern_first_char) continue;
2606 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002607 int j = 1;
2608 do {
2609 if (pattern[j] != subject[i+j]) {
2610 break;
2611 }
2612 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002613 } while (j < pattern.length());
2614 if (j == pattern.length()) {
2615 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002616 return i;
2617 }
2618 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002619 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002621 return -1;
2622}
2623
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002624// Simple indexOf that never bails out. For short patterns only.
2625template <typename pchar, typename schar>
2626static int SimpleIndexOf(Vector<const schar> subject,
2627 Vector<const pchar> pattern,
2628 int idx) {
2629 pchar pattern_first_char = pattern[0];
2630 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002631 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2632 const schar* pos = reinterpret_cast<const schar*>(
2633 memchr(subject.start() + i,
2634 pattern_first_char,
2635 n - i + 1));
2636 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002637 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002638 } else {
2639 if (subject[i] != pattern_first_char) continue;
2640 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002641 int j = 1;
2642 do {
2643 if (pattern[j] != subject[i+j]) {
2644 break;
2645 }
2646 j++;
2647 } while (j < pattern.length());
2648 if (j == pattern.length()) {
2649 return i;
2650 }
2651 }
2652 return -1;
2653}
2654
2655
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002656// Strategy for searching for a string in another string.
2657enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002658
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002659
2660template <typename pchar>
2661static inline StringSearchStrategy InitializeStringSearch(
2662 Vector<const pchar> pat, bool ascii_subject) {
2663 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002664 // We have an ASCII haystack and a non-ASCII needle. Check if there
2665 // really is a non-ASCII character in the needle and bail out if there
2666 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002667 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002668 for (int i = 0; i < pat.length(); i++) {
2669 uc16 c = pat[i];
2670 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002671 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002672 }
2673 }
2674 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002675 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002676 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002677 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002678 algorithm = SIMPLE_SEARCH;
2679 return SEARCH_LONG;
2680}
2681
2682
2683// Dispatch long needle searches to different algorithms.
2684template <typename schar, typename pchar>
2685static int ComplexIndexOf(Vector<const schar> sub,
2686 Vector<const pchar> pat,
2687 int start_index) {
2688 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002689 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002690 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002691 int idx = start_index;
2692 switch (algorithm) {
2693 case SIMPLE_SEARCH:
2694 idx = SimpleIndexOf(sub, pat, idx, &complete);
2695 if (complete) return idx;
2696 BoyerMoorePopulateBadCharTable(pat);
2697 algorithm = BOYER_MOORE_HORSPOOL;
2698 // FALLTHROUGH.
2699 case BOYER_MOORE_HORSPOOL:
2700 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2701 if (complete) return idx;
2702 // Build the Good Suffix table and continue searching.
2703 BoyerMoorePopulateGoodSuffixTable(pat);
2704 algorithm = BOYER_MOORE;
2705 // FALLTHROUGH.
2706 case BOYER_MOORE:
2707 return BoyerMooreIndexOf(sub, pat, idx);
2708 }
2709 UNREACHABLE();
2710 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002711}
2712
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002713
2714// Dispatch to different search strategies for a single search.
2715// If searching multiple times on the same needle, the search
2716// strategy should only be computed once and then dispatch to different
2717// loops.
2718template <typename schar, typename pchar>
2719static int StringSearch(Vector<const schar> sub,
2720 Vector<const pchar> pat,
2721 int start_index) {
2722 bool ascii_subject = (sizeof(schar) == 1);
2723 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2724 switch (strategy) {
2725 case SEARCH_FAIL: return -1;
2726 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2727 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2728 }
2729 UNREACHABLE();
2730 return -1;
2731}
2732
2733
ager@chromium.org7c537e22008-10-16 08:43:32 +00002734// Perform string match of pattern on subject, starting at start index.
2735// Caller must ensure that 0 <= start_index <= sub->length(),
2736// and should check that pat->length() + start_index <= sub->length()
2737int Runtime::StringMatch(Handle<String> sub,
2738 Handle<String> pat,
2739 int start_index) {
2740 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002741 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002742
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002743 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002744 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002745
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002746 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002747 if (start_index + pattern_length > subject_length) return -1;
2748
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002749 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002750 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002751 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002752
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002753 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002754 // character patterns linear search is necessary, so any smart
2755 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002756 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002757 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002758 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002759 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002760 if (pchar > String::kMaxAsciiCharCode) {
2761 return -1;
2762 }
2763 Vector<const char> ascii_vector =
2764 sub->ToAsciiVector().SubVector(start_index, subject_length);
2765 const void* pos = memchr(ascii_vector.start(),
2766 static_cast<const char>(pchar),
2767 static_cast<size_t>(ascii_vector.length()));
2768 if (pos == NULL) {
2769 return -1;
2770 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002771 return static_cast<int>(reinterpret_cast<const char*>(pos)
2772 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002773 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002774 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002775 }
2776
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002777 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002778 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002779 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002780
ager@chromium.org7c537e22008-10-16 08:43:32 +00002781 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2782 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002783 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002784 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002785 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002786 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002787 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002788 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002789 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002790 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002791 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002792 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002793 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002794 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002795}
2796
2797
2798static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002799 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002800 ASSERT(args.length() == 3);
2801
ager@chromium.org7c537e22008-10-16 08:43:32 +00002802 CONVERT_ARG_CHECKED(String, sub, 0);
2803 CONVERT_ARG_CHECKED(String, pat, 1);
2804
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002805 Object* index = args[2];
2806 uint32_t start_index;
2807 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2808
ager@chromium.org870a0b62008-11-04 11:43:05 +00002809 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002810 int position = Runtime::StringMatch(sub, pat, start_index);
2811 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002812}
2813
2814
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002815template <typename schar, typename pchar>
2816static int StringMatchBackwards(Vector<const schar> sub,
2817 Vector<const pchar> pat,
2818 int idx) {
2819 ASSERT(pat.length() >= 1);
2820 ASSERT(idx + pat.length() <= sub.length());
2821
2822 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2823 for (int i = 0; i < pat.length(); i++) {
2824 uc16 c = pat[i];
2825 if (c > String::kMaxAsciiCharCode) {
2826 return -1;
2827 }
2828 }
2829 }
2830
2831 pchar pattern_first_char = pat[0];
2832 for (int i = idx; i >= 0; i--) {
2833 if (sub[i] != pattern_first_char) continue;
2834 int j = 1;
2835 while (j < pat.length()) {
2836 if (pat[j] != sub[i+j]) {
2837 break;
2838 }
2839 j++;
2840 }
2841 if (j == pat.length()) {
2842 return i;
2843 }
2844 }
2845 return -1;
2846}
2847
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002848static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002849 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002850 ASSERT(args.length() == 3);
2851
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002852 CONVERT_ARG_CHECKED(String, sub, 0);
2853 CONVERT_ARG_CHECKED(String, pat, 1);
2854
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002855 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002856 uint32_t start_index;
2857 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2858
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002859 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002860 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002861
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002862 if (start_index + pat_length > sub_length) {
2863 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002864 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002865
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002866 if (pat_length == 0) {
2867 return Smi::FromInt(start_index);
2868 }
2869
2870 if (!sub->IsFlat()) {
2871 FlattenString(sub);
2872 }
2873
2874 if (pat_length == 1) {
2875 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2876 if (sub->IsAsciiRepresentation()) {
2877 uc16 pchar = pat->Get(0);
2878 if (pchar > String::kMaxAsciiCharCode) {
2879 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002880 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002881 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2882 static_cast<char>(pat->Get(0)),
2883 start_index));
2884 } else {
2885 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2886 pat->Get(0),
2887 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002888 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002889 }
2890
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002891 if (!pat->IsFlat()) {
2892 FlattenString(pat);
2893 }
2894
2895 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2896
2897 int position = -1;
2898
2899 if (pat->IsAsciiRepresentation()) {
2900 Vector<const char> pat_vector = pat->ToAsciiVector();
2901 if (sub->IsAsciiRepresentation()) {
2902 position = StringMatchBackwards(sub->ToAsciiVector(),
2903 pat_vector,
2904 start_index);
2905 } else {
2906 position = StringMatchBackwards(sub->ToUC16Vector(),
2907 pat_vector,
2908 start_index);
2909 }
2910 } else {
2911 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2912 if (sub->IsAsciiRepresentation()) {
2913 position = StringMatchBackwards(sub->ToAsciiVector(),
2914 pat_vector,
2915 start_index);
2916 } else {
2917 position = StringMatchBackwards(sub->ToUC16Vector(),
2918 pat_vector,
2919 start_index);
2920 }
2921 }
2922
2923 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002924}
2925
2926
2927static Object* Runtime_StringLocaleCompare(Arguments args) {
2928 NoHandleAllocation ha;
2929 ASSERT(args.length() == 2);
2930
2931 CONVERT_CHECKED(String, str1, args[0]);
2932 CONVERT_CHECKED(String, str2, args[1]);
2933
2934 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002935 int str1_length = str1->length();
2936 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002937
2938 // Decide trivial cases without flattening.
2939 if (str1_length == 0) {
2940 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2941 return Smi::FromInt(-str2_length);
2942 } else {
2943 if (str2_length == 0) return Smi::FromInt(str1_length);
2944 }
2945
2946 int end = str1_length < str2_length ? str1_length : str2_length;
2947
2948 // No need to flatten if we are going to find the answer on the first
2949 // character. At this point we know there is at least one character
2950 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002951 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002952 if (d != 0) return Smi::FromInt(d);
2953
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002954 str1->TryFlatten();
2955 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002956
2957 static StringInputBuffer buf1;
2958 static StringInputBuffer buf2;
2959
2960 buf1.Reset(str1);
2961 buf2.Reset(str2);
2962
2963 for (int i = 0; i < end; i++) {
2964 uint16_t char1 = buf1.GetNext();
2965 uint16_t char2 = buf2.GetNext();
2966 if (char1 != char2) return Smi::FromInt(char1 - char2);
2967 }
2968
2969 return Smi::FromInt(str1_length - str2_length);
2970}
2971
2972
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002973static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002974 NoHandleAllocation ha;
2975 ASSERT(args.length() == 3);
2976
2977 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002978 Object* from = args[1];
2979 Object* to = args[2];
2980 int start, end;
2981 // We have a fast integer-only case here to avoid a conversion to double in
2982 // the common case where from and to are Smis.
2983 if (from->IsSmi() && to->IsSmi()) {
2984 start = Smi::cast(from)->value();
2985 end = Smi::cast(to)->value();
2986 } else {
2987 CONVERT_DOUBLE_CHECKED(from_number, from);
2988 CONVERT_DOUBLE_CHECKED(to_number, to);
2989 start = FastD2I(from_number);
2990 end = FastD2I(to_number);
2991 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002992 RUNTIME_ASSERT(end >= start);
2993 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002994 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002995 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002996 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002997}
2998
2999
ager@chromium.org41826e72009-03-30 13:30:57 +00003000static Object* Runtime_StringMatch(Arguments args) {
3001 ASSERT_EQ(3, args.length());
3002
3003 CONVERT_ARG_CHECKED(String, subject, 0);
3004 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3005 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3006 HandleScope handles;
3007
3008 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3009
3010 if (match.is_null()) {
3011 return Failure::Exception();
3012 }
3013 if (match->IsNull()) {
3014 return Heap::null_value();
3015 }
3016 int length = subject->length();
3017
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003018 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003019 ZoneList<int> offsets(8);
3020 do {
3021 int start;
3022 int end;
3023 {
3024 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003025 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003026 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3027 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3028 }
3029 offsets.Add(start);
3030 offsets.Add(end);
3031 int index = start < end ? end : end + 1;
3032 if (index > length) break;
3033 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3034 if (match.is_null()) {
3035 return Failure::Exception();
3036 }
3037 } while (!match->IsNull());
3038 int matches = offsets.length() / 2;
3039 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3040 for (int i = 0; i < matches ; i++) {
3041 int from = offsets.at(i * 2);
3042 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003043 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003044 }
3045 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3046 result->set_length(Smi::FromInt(matches));
3047 return *result;
3048}
3049
3050
lrn@chromium.org25156de2010-04-06 13:10:27 +00003051// Two smis before and after the match, for very long strings.
3052const int kMaxBuilderEntriesPerRegExpMatch = 5;
3053
3054
3055static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3056 Handle<JSArray> last_match_info,
3057 int match_start,
3058 int match_end) {
3059 // Fill last_match_info with a single capture.
3060 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3061 AssertNoAllocation no_gc;
3062 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3063 RegExpImpl::SetLastCaptureCount(elements, 2);
3064 RegExpImpl::SetLastInput(elements, *subject);
3065 RegExpImpl::SetLastSubject(elements, *subject);
3066 RegExpImpl::SetCapture(elements, 0, match_start);
3067 RegExpImpl::SetCapture(elements, 1, match_end);
3068}
3069
3070
3071template <typename schar>
3072static bool SearchCharMultiple(Vector<schar> subject,
3073 String* pattern,
3074 schar pattern_char,
3075 FixedArrayBuilder* builder,
3076 int* match_pos) {
3077 // Position of last match.
3078 int pos = *match_pos;
3079 int subject_length = subject.length();
3080 while (pos < subject_length) {
3081 int match_end = pos + 1;
3082 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3083 *match_pos = pos;
3084 return false;
3085 }
3086 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3087 if (new_pos >= 0) {
3088 // Match has been found.
3089 if (new_pos > match_end) {
3090 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3091 }
3092 pos = new_pos;
3093 builder->Add(pattern);
3094 } else {
3095 break;
3096 }
3097 }
3098 if (pos + 1 < subject_length) {
3099 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3100 }
3101 *match_pos = pos;
3102 return true;
3103}
3104
3105
3106static bool SearchCharMultiple(Handle<String> subject,
3107 Handle<String> pattern,
3108 Handle<JSArray> last_match_info,
3109 FixedArrayBuilder* builder) {
3110 ASSERT(subject->IsFlat());
3111 ASSERT_EQ(1, pattern->length());
3112 uc16 pattern_char = pattern->Get(0);
3113 // Treating position before first as initial "previous match position".
3114 int match_pos = -1;
3115
3116 for (;;) { // Break when search complete.
3117 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3118 AssertNoAllocation no_gc;
3119 if (subject->IsAsciiRepresentation()) {
3120 if (pattern_char > String::kMaxAsciiCharCode) {
3121 break;
3122 }
3123 Vector<const char> subject_vector = subject->ToAsciiVector();
3124 char pattern_ascii_char = static_cast<char>(pattern_char);
3125 bool complete = SearchCharMultiple<const char>(subject_vector,
3126 *pattern,
3127 pattern_ascii_char,
3128 builder,
3129 &match_pos);
3130 if (complete) break;
3131 } else {
3132 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3133 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3134 *pattern,
3135 pattern_char,
3136 builder,
3137 &match_pos);
3138 if (complete) break;
3139 }
3140 }
3141
3142 if (match_pos >= 0) {
3143 SetLastMatchInfoNoCaptures(subject,
3144 last_match_info,
3145 match_pos,
3146 match_pos + 1);
3147 return true;
3148 }
3149 return false; // No matches at all.
3150}
3151
3152
3153template <typename schar, typename pchar>
3154static bool SearchStringMultiple(Vector<schar> subject,
3155 String* pattern,
3156 Vector<pchar> pattern_string,
3157 FixedArrayBuilder* builder,
3158 int* match_pos) {
3159 int pos = *match_pos;
3160 int subject_length = subject.length();
3161 int pattern_length = pattern_string.length();
3162 int max_search_start = subject_length - pattern_length;
3163 bool is_ascii = (sizeof(schar) == 1);
3164 StringSearchStrategy strategy =
3165 InitializeStringSearch(pattern_string, is_ascii);
3166 switch (strategy) {
3167 case SEARCH_FAIL: return false;
3168 case SEARCH_SHORT:
3169 while (pos <= max_search_start) {
3170 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3171 *match_pos = pos;
3172 return false;
3173 }
3174 // Position of end of previous match.
3175 int match_end = pos + pattern_length;
3176 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3177 if (new_pos >= 0) {
3178 // A match.
3179 if (new_pos > match_end) {
3180 ReplacementStringBuilder::AddSubjectSlice(builder,
3181 match_end,
3182 new_pos);
3183 }
3184 pos = new_pos;
3185 builder->Add(pattern);
3186 } else {
3187 break;
3188 }
3189 }
3190 break;
3191 case SEARCH_LONG:
3192 while (pos <= max_search_start) {
3193 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3194 *match_pos = pos;
3195 return false;
3196 }
3197 int new_pos = ComplexIndexOf(subject,
3198 pattern_string,
3199 pos + pattern_length);
3200 if (new_pos >= 0) {
3201 // A match has been found.
3202 if (new_pos > pos) {
3203 ReplacementStringBuilder::AddSubjectSlice(builder, pos, new_pos);
3204 }
3205 pos = new_pos;
3206 builder->Add(pattern);
3207 } else {
3208 break;
3209 }
3210 }
3211 break;
3212 }
3213 if (pos < max_search_start) {
3214 ReplacementStringBuilder::AddSubjectSlice(builder,
3215 pos + pattern_length,
3216 subject_length);
3217 }
3218 *match_pos = pos;
3219 return true;
3220}
3221
3222
3223static bool SearchStringMultiple(Handle<String> subject,
3224 Handle<String> pattern,
3225 Handle<JSArray> last_match_info,
3226 FixedArrayBuilder* builder) {
3227 ASSERT(subject->IsFlat());
3228 ASSERT(pattern->IsFlat());
3229 ASSERT(pattern->length() > 1);
3230
3231 // Treating as if a previous match was before first character.
3232 int match_pos = -pattern->length();
3233
3234 for (;;) { // Break when search complete.
3235 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3236 AssertNoAllocation no_gc;
3237 if (subject->IsAsciiRepresentation()) {
3238 Vector<const char> subject_vector = subject->ToAsciiVector();
3239 if (pattern->IsAsciiRepresentation()) {
3240 if (SearchStringMultiple(subject_vector,
3241 *pattern,
3242 pattern->ToAsciiVector(),
3243 builder,
3244 &match_pos)) break;
3245 } else {
3246 if (SearchStringMultiple(subject_vector,
3247 *pattern,
3248 pattern->ToUC16Vector(),
3249 builder,
3250 &match_pos)) break;
3251 }
3252 } else {
3253 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3254 if (pattern->IsAsciiRepresentation()) {
3255 if (SearchStringMultiple(subject_vector,
3256 *pattern,
3257 pattern->ToAsciiVector(),
3258 builder,
3259 &match_pos)) break;
3260 } else {
3261 if (SearchStringMultiple(subject_vector,
3262 *pattern,
3263 pattern->ToUC16Vector(),
3264 builder,
3265 &match_pos)) break;
3266 }
3267 }
3268 }
3269
3270 if (match_pos >= 0) {
3271 SetLastMatchInfoNoCaptures(subject,
3272 last_match_info,
3273 match_pos,
3274 match_pos + pattern->length());
3275 return true;
3276 }
3277 return false; // No matches at all.
3278}
3279
3280
3281static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3282 Handle<String> subject,
3283 Handle<JSRegExp> regexp,
3284 Handle<JSArray> last_match_array,
3285 FixedArrayBuilder* builder) {
3286 ASSERT(subject->IsFlat());
3287 int match_start = -1;
3288 int match_end = 0;
3289 int pos = 0;
3290 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3291 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3292
3293 OffsetsVector registers(required_registers);
3294 Vector<int> register_vector(registers.vector(), registers.length());
3295 int subject_length = subject->length();
3296
3297 for (;;) { // Break on failure, return on exception.
3298 RegExpImpl::IrregexpResult result =
3299 RegExpImpl::IrregexpExecOnce(regexp,
3300 subject,
3301 pos,
3302 register_vector);
3303 if (result == RegExpImpl::RE_SUCCESS) {
3304 match_start = register_vector[0];
3305 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3306 if (match_end < match_start) {
3307 ReplacementStringBuilder::AddSubjectSlice(builder,
3308 match_end,
3309 match_start);
3310 }
3311 match_end = register_vector[1];
3312 HandleScope loop_scope;
3313 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3314 if (match_start != match_end) {
3315 pos = match_end;
3316 } else {
3317 pos = match_end + 1;
3318 if (pos > subject_length) break;
3319 }
3320 } else if (result == RegExpImpl::RE_FAILURE) {
3321 break;
3322 } else {
3323 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3324 return result;
3325 }
3326 }
3327
3328 if (match_start >= 0) {
3329 if (match_end < subject_length) {
3330 ReplacementStringBuilder::AddSubjectSlice(builder,
3331 match_end,
3332 subject_length);
3333 }
3334 SetLastMatchInfoNoCaptures(subject,
3335 last_match_array,
3336 match_start,
3337 match_end);
3338 return RegExpImpl::RE_SUCCESS;
3339 } else {
3340 return RegExpImpl::RE_FAILURE; // No matches at all.
3341 }
3342}
3343
3344
3345static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3346 Handle<String> subject,
3347 Handle<JSRegExp> regexp,
3348 Handle<JSArray> last_match_array,
3349 FixedArrayBuilder* builder) {
3350
3351 ASSERT(subject->IsFlat());
3352 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3353 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3354
3355 OffsetsVector registers(required_registers);
3356 Vector<int> register_vector(registers.vector(), registers.length());
3357
3358 RegExpImpl::IrregexpResult result =
3359 RegExpImpl::IrregexpExecOnce(regexp,
3360 subject,
3361 0,
3362 register_vector);
3363
3364 int capture_count = regexp->CaptureCount();
3365 int subject_length = subject->length();
3366
3367 // Position to search from.
3368 int pos = 0;
3369 // End of previous match. Differs from pos if match was empty.
3370 int match_end = 0;
3371 if (result == RegExpImpl::RE_SUCCESS) {
3372 // Need to keep a copy of the previous match for creating last_match_info
3373 // at the end, so we have two vectors that we swap between.
3374 OffsetsVector registers2(required_registers);
3375 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3376
3377 do {
3378 int match_start = register_vector[0];
3379 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3380 if (match_end < match_start) {
3381 ReplacementStringBuilder::AddSubjectSlice(builder,
3382 match_end,
3383 match_start);
3384 }
3385 match_end = register_vector[1];
3386
3387 {
3388 // Avoid accumulating new handles inside loop.
3389 HandleScope temp_scope;
3390 // Arguments array to replace function is match, captures, index and
3391 // subject, i.e., 3 + capture count in total.
3392 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3393 elements->set(0, *Factory::NewSubString(subject,
3394 match_start,
3395 match_end));
3396 for (int i = 1; i <= capture_count; i++) {
3397 int start = register_vector[i * 2];
3398 if (start >= 0) {
3399 int end = register_vector[i * 2 + 1];
3400 ASSERT(start <= end);
3401 Handle<String> substring = Factory::NewSubString(subject,
3402 start,
3403 end);
3404 elements->set(i, *substring);
3405 } else {
3406 ASSERT(register_vector[i * 2 + 1] < 0);
3407 elements->set(i, Heap::undefined_value());
3408 }
3409 }
3410 elements->set(capture_count + 1, Smi::FromInt(match_start));
3411 elements->set(capture_count + 2, *subject);
3412 builder->Add(*Factory::NewJSArrayWithElements(elements));
3413 }
3414 // Swap register vectors, so the last successful match is in
3415 // prev_register_vector.
3416 Vector<int> tmp = prev_register_vector;
3417 prev_register_vector = register_vector;
3418 register_vector = tmp;
3419
3420 if (match_end > match_start) {
3421 pos = match_end;
3422 } else {
3423 pos = match_end + 1;
3424 if (pos > subject_length) {
3425 break;
3426 }
3427 }
3428
3429 result = RegExpImpl::IrregexpExecOnce(regexp,
3430 subject,
3431 pos,
3432 register_vector);
3433 } while (result == RegExpImpl::RE_SUCCESS);
3434
3435 if (result != RegExpImpl::RE_EXCEPTION) {
3436 // Finished matching, with at least one match.
3437 if (match_end < subject_length) {
3438 ReplacementStringBuilder::AddSubjectSlice(builder,
3439 match_end,
3440 subject_length);
3441 }
3442
3443 int last_match_capture_count = (capture_count + 1) * 2;
3444 int last_match_array_size =
3445 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3446 last_match_array->EnsureSize(last_match_array_size);
3447 AssertNoAllocation no_gc;
3448 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3449 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3450 RegExpImpl::SetLastSubject(elements, *subject);
3451 RegExpImpl::SetLastInput(elements, *subject);
3452 for (int i = 0; i < last_match_capture_count; i++) {
3453 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3454 }
3455 return RegExpImpl::RE_SUCCESS;
3456 }
3457 }
3458 // No matches at all, return failure or exception result directly.
3459 return result;
3460}
3461
3462
3463static Object* Runtime_RegExpExecMultiple(Arguments args) {
3464 ASSERT(args.length() == 4);
3465 HandleScope handles;
3466
3467 CONVERT_ARG_CHECKED(String, subject, 1);
3468 if (!subject->IsFlat()) { FlattenString(subject); }
3469 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3470 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3471 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3472
3473 ASSERT(last_match_info->HasFastElements());
3474 ASSERT(regexp->GetFlags().is_global());
3475 Handle<FixedArray> result_elements;
3476 if (result_array->HasFastElements()) {
3477 result_elements =
3478 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3479 } else {
3480 result_elements = Factory::NewFixedArrayWithHoles(16);
3481 }
3482 FixedArrayBuilder builder(result_elements);
3483
3484 if (regexp->TypeTag() == JSRegExp::ATOM) {
3485 Handle<String> pattern(
3486 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3487 int pattern_length = pattern->length();
3488 if (pattern_length == 1) {
3489 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3490 return *builder.ToJSArray(result_array);
3491 }
3492 return Heap::null_value();
3493 }
3494
3495 if (!pattern->IsFlat()) FlattenString(pattern);
3496 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3497 return *builder.ToJSArray(result_array);
3498 }
3499 return Heap::null_value();
3500 }
3501
3502 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3503
3504 RegExpImpl::IrregexpResult result;
3505 if (regexp->CaptureCount() == 0) {
3506 result = SearchRegExpNoCaptureMultiple(subject,
3507 regexp,
3508 last_match_info,
3509 &builder);
3510 } else {
3511 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3512 }
3513 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3514 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3515 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3516 return Failure::Exception();
3517}
3518
3519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003520static Object* Runtime_NumberToRadixString(Arguments args) {
3521 NoHandleAllocation ha;
3522 ASSERT(args.length() == 2);
3523
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003524 // Fast case where the result is a one character string.
3525 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3526 int value = Smi::cast(args[0])->value();
3527 int radix = Smi::cast(args[1])->value();
3528 if (value >= 0 && value < radix) {
3529 RUNTIME_ASSERT(radix <= 36);
3530 // Character array used for conversion.
3531 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3532 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3533 }
3534 }
3535
3536 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003537 CONVERT_DOUBLE_CHECKED(value, args[0]);
3538 if (isnan(value)) {
3539 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3540 }
3541 if (isinf(value)) {
3542 if (value < 0) {
3543 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3544 }
3545 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3546 }
3547 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3548 int radix = FastD2I(radix_number);
3549 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3550 char* str = DoubleToRadixCString(value, radix);
3551 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3552 DeleteArray(str);
3553 return result;
3554}
3555
3556
3557static Object* Runtime_NumberToFixed(Arguments args) {
3558 NoHandleAllocation ha;
3559 ASSERT(args.length() == 2);
3560
3561 CONVERT_DOUBLE_CHECKED(value, args[0]);
3562 if (isnan(value)) {
3563 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3564 }
3565 if (isinf(value)) {
3566 if (value < 0) {
3567 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3568 }
3569 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3570 }
3571 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3572 int f = FastD2I(f_number);
3573 RUNTIME_ASSERT(f >= 0);
3574 char* str = DoubleToFixedCString(value, f);
3575 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3576 DeleteArray(str);
3577 return res;
3578}
3579
3580
3581static Object* Runtime_NumberToExponential(Arguments args) {
3582 NoHandleAllocation ha;
3583 ASSERT(args.length() == 2);
3584
3585 CONVERT_DOUBLE_CHECKED(value, args[0]);
3586 if (isnan(value)) {
3587 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3588 }
3589 if (isinf(value)) {
3590 if (value < 0) {
3591 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3592 }
3593 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3594 }
3595 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3596 int f = FastD2I(f_number);
3597 RUNTIME_ASSERT(f >= -1 && f <= 20);
3598 char* str = DoubleToExponentialCString(value, f);
3599 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3600 DeleteArray(str);
3601 return res;
3602}
3603
3604
3605static Object* Runtime_NumberToPrecision(Arguments args) {
3606 NoHandleAllocation ha;
3607 ASSERT(args.length() == 2);
3608
3609 CONVERT_DOUBLE_CHECKED(value, args[0]);
3610 if (isnan(value)) {
3611 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3612 }
3613 if (isinf(value)) {
3614 if (value < 0) {
3615 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3616 }
3617 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3618 }
3619 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3620 int f = FastD2I(f_number);
3621 RUNTIME_ASSERT(f >= 1 && f <= 21);
3622 char* str = DoubleToPrecisionCString(value, f);
3623 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3624 DeleteArray(str);
3625 return res;
3626}
3627
3628
3629// Returns a single character string where first character equals
3630// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003631static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003632 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003633 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003634 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003635 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003636 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003637 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003638}
3639
3640
3641Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3642 // Handle [] indexing on Strings
3643 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003644 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3645 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003646 }
3647
3648 // Handle [] indexing on String objects
3649 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003650 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3651 Handle<Object> result =
3652 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3653 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003654 }
3655
3656 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003657 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003658 return prototype->GetElement(index);
3659 }
3660
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003661 return GetElement(object, index);
3662}
3663
3664
3665Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003666 return object->GetElement(index);
3667}
3668
3669
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003670Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3671 HandleScope scope;
3672
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003673 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003674 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003675 Handle<Object> error =
3676 Factory::NewTypeError("non_object_property_load",
3677 HandleVector(args, 2));
3678 return Top::Throw(*error);
3679 }
3680
3681 // Check if the given key is an array index.
3682 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003683 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003684 return GetElementOrCharAt(object, index);
3685 }
3686
3687 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003688 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003689 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003690 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003691 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003692 bool has_pending_exception = false;
3693 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003694 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003695 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003696 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003697 }
3698
ager@chromium.org32912102009-01-16 10:38:43 +00003699 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003700 // the element if so.
3701 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003702 return GetElementOrCharAt(object, index);
3703 } else {
3704 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003705 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003706 }
3707}
3708
3709
3710static Object* Runtime_GetProperty(Arguments args) {
3711 NoHandleAllocation ha;
3712 ASSERT(args.length() == 2);
3713
3714 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003715 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003716
3717 return Runtime::GetObjectProperty(object, key);
3718}
3719
3720
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003721// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003722static Object* Runtime_KeyedGetProperty(Arguments args) {
3723 NoHandleAllocation ha;
3724 ASSERT(args.length() == 2);
3725
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003726 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003727 // itself.
3728 //
3729 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003730 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003731 // global proxy object never has properties. This is the case
3732 // because the global proxy object forwards everything to its hidden
3733 // prototype including local lookups.
3734 //
3735 // Additionally, we need to make sure that we do not cache results
3736 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003737 if (args[0]->IsJSObject() &&
3738 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003739 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003740 args[1]->IsString()) {
3741 JSObject* receiver = JSObject::cast(args[0]);
3742 String* key = String::cast(args[1]);
3743 if (receiver->HasFastProperties()) {
3744 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003745 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003746 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3747 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003748 Object* value = receiver->FastPropertyAt(offset);
3749 return value->IsTheHole() ? Heap::undefined_value() : value;
3750 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003751 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003752 LookupResult result;
3753 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003754 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003755 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003756 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003757 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003758 }
3759 } else {
3760 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003761 StringDictionary* dictionary = receiver->property_dictionary();
3762 int entry = dictionary->FindEntry(key);
3763 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003764 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003765 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003766 if (!receiver->IsGlobalObject()) return value;
3767 value = JSGlobalPropertyCell::cast(value)->value();
3768 if (!value->IsTheHole()) return value;
3769 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003770 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003771 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003772 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3773 // Fast case for string indexing using [] with a smi index.
3774 HandleScope scope;
3775 Handle<String> str = args.at<String>(0);
3776 int index = Smi::cast(args[1])->value();
3777 Handle<Object> result = GetCharAt(str, index);
3778 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003779 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003780
3781 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003782 return Runtime::GetObjectProperty(args.at<Object>(0),
3783 args.at<Object>(1));
3784}
3785
3786
ager@chromium.org5c838252010-02-19 08:53:10 +00003787static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3788 ASSERT(args.length() == 5);
3789 HandleScope scope;
3790 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3791 CONVERT_CHECKED(String, name, args[1]);
3792 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3793 CONVERT_CHECKED(JSFunction, fun, args[3]);
3794 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3795 int unchecked = flag_attr->value();
3796 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3797 RUNTIME_ASSERT(!obj->IsNull());
3798 LookupResult result;
3799 obj->LocalLookupRealNamedProperty(name, &result);
3800
3801 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3802 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3803 // delete it to avoid running into trouble in DefineAccessor, which
3804 // handles this incorrectly if the property is readonly (does nothing)
3805 if (result.IsProperty() &&
3806 (result.type() == FIELD || result.type() == NORMAL
3807 || result.type() == CONSTANT_FUNCTION)) {
3808 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3809 }
3810 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3811}
3812
3813static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3814 ASSERT(args.length() == 4);
3815 HandleScope scope;
3816 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3817 CONVERT_ARG_CHECKED(String, name, 1);
3818 Handle<Object> obj_value = args.at<Object>(2);
3819
3820 CONVERT_CHECKED(Smi, flag, args[3]);
3821 int unchecked = flag->value();
3822 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3823
3824 LookupResult result;
3825 js_object->LocalLookupRealNamedProperty(*name, &result);
3826
3827 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3828
3829 // Take special care when attributes are different and there is already
3830 // a property. For simplicity we normalize the property which enables us
3831 // to not worry about changing the instance_descriptor and creating a new
3832 // map. The current version of SetObjectProperty does not handle attributes
3833 // correctly in the case where a property is a field and is reset with
3834 // new attributes.
3835 if (result.IsProperty() && attr != result.GetAttributes()) {
3836 // New attributes - normalize to avoid writing to instance descriptor
3837 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3838 // Use IgnoreAttributes version since a readonly property may be
3839 // overridden and SetProperty does not allow this.
3840 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3841 *obj_value,
3842 attr);
3843 }
3844 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3845}
3846
3847
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003848Object* Runtime::SetObjectProperty(Handle<Object> object,
3849 Handle<Object> key,
3850 Handle<Object> value,
3851 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003852 HandleScope scope;
3853
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003854 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003855 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003856 Handle<Object> error =
3857 Factory::NewTypeError("non_object_property_store",
3858 HandleVector(args, 2));
3859 return Top::Throw(*error);
3860 }
3861
3862 // If the object isn't a JavaScript object, we ignore the store.
3863 if (!object->IsJSObject()) return *value;
3864
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003865 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3866
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003867 // Check if the given key is an array index.
3868 uint32_t index;
3869 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003870 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3871 // of a string using [] notation. We need to support this too in
3872 // JavaScript.
3873 // In the case of a String object we just need to redirect the assignment to
3874 // the underlying string if the index is in range. Since the underlying
3875 // string does nothing with the assignment then we can ignore such
3876 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003877 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003878 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003879 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003880
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003881 Handle<Object> result = SetElement(js_object, index, value);
3882 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003883 return *value;
3884 }
3885
3886 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003887 Handle<Object> result;
3888 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003889 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003890 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003891 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003892 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003893 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003894 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003895 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003896 return *value;
3897 }
3898
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003899 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003900 bool has_pending_exception = false;
3901 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3902 if (has_pending_exception) return Failure::Exception();
3903 Handle<String> name = Handle<String>::cast(converted);
3904
3905 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003906 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003907 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003908 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003909 }
3910}
3911
3912
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003913Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3914 Handle<Object> key,
3915 Handle<Object> value,
3916 PropertyAttributes attr) {
3917 HandleScope scope;
3918
3919 // Check if the given key is an array index.
3920 uint32_t index;
3921 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003922 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3923 // of a string using [] notation. We need to support this too in
3924 // JavaScript.
3925 // In the case of a String object we just need to redirect the assignment to
3926 // the underlying string if the index is in range. Since the underlying
3927 // string does nothing with the assignment then we can ignore such
3928 // assignments.
3929 if (js_object->IsStringObjectWithCharacterAt(index)) {
3930 return *value;
3931 }
3932
3933 return js_object->SetElement(index, *value);
3934 }
3935
3936 if (key->IsString()) {
3937 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003938 return js_object->SetElement(index, *value);
3939 } else {
3940 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003941 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003942 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3943 *value,
3944 attr);
3945 }
3946 }
3947
3948 // Call-back into JavaScript to convert the key to a string.
3949 bool has_pending_exception = false;
3950 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3951 if (has_pending_exception) return Failure::Exception();
3952 Handle<String> name = Handle<String>::cast(converted);
3953
3954 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003955 return js_object->SetElement(index, *value);
3956 } else {
3957 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3958 }
3959}
3960
3961
ager@chromium.orge2902be2009-06-08 12:21:35 +00003962Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3963 Handle<Object> key) {
3964 HandleScope scope;
3965
3966 // Check if the given key is an array index.
3967 uint32_t index;
3968 if (Array::IndexFromObject(*key, &index)) {
3969 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3970 // characters of a string using [] notation. In the case of a
3971 // String object we just need to redirect the deletion to the
3972 // underlying string if the index is in range. Since the
3973 // underlying string does nothing with the deletion, we can ignore
3974 // such deletions.
3975 if (js_object->IsStringObjectWithCharacterAt(index)) {
3976 return Heap::true_value();
3977 }
3978
3979 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3980 }
3981
3982 Handle<String> key_string;
3983 if (key->IsString()) {
3984 key_string = Handle<String>::cast(key);
3985 } else {
3986 // Call-back into JavaScript to convert the key to a string.
3987 bool has_pending_exception = false;
3988 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3989 if (has_pending_exception) return Failure::Exception();
3990 key_string = Handle<String>::cast(converted);
3991 }
3992
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003993 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003994 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3995}
3996
3997
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003998static Object* Runtime_SetProperty(Arguments args) {
3999 NoHandleAllocation ha;
4000 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4001
4002 Handle<Object> object = args.at<Object>(0);
4003 Handle<Object> key = args.at<Object>(1);
4004 Handle<Object> value = args.at<Object>(2);
4005
4006 // Compute attributes.
4007 PropertyAttributes attributes = NONE;
4008 if (args.length() == 4) {
4009 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004010 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004011 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004012 RUNTIME_ASSERT(
4013 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4014 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004015 }
4016 return Runtime::SetObjectProperty(object, key, value, attributes);
4017}
4018
4019
4020// Set a local property, even if it is READ_ONLY. If the property does not
4021// exist, it will be added with attributes NONE.
4022static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4023 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004024 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004025 CONVERT_CHECKED(JSObject, object, args[0]);
4026 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004027 // Compute attributes.
4028 PropertyAttributes attributes = NONE;
4029 if (args.length() == 4) {
4030 CONVERT_CHECKED(Smi, value_obj, args[3]);
4031 int unchecked_value = value_obj->value();
4032 // Only attribute bits should be set.
4033 RUNTIME_ASSERT(
4034 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4035 attributes = static_cast<PropertyAttributes>(unchecked_value);
4036 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004037
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004038 return object->
4039 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004040}
4041
4042
4043static Object* Runtime_DeleteProperty(Arguments args) {
4044 NoHandleAllocation ha;
4045 ASSERT(args.length() == 2);
4046
4047 CONVERT_CHECKED(JSObject, object, args[0]);
4048 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004049 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004050}
4051
4052
ager@chromium.org9085a012009-05-11 19:22:57 +00004053static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4054 Handle<String> key) {
4055 if (object->HasLocalProperty(*key)) return Heap::true_value();
4056 // Handle hidden prototypes. If there's a hidden prototype above this thing
4057 // then we have to check it for properties, because they are supposed to
4058 // look like they are on this object.
4059 Handle<Object> proto(object->GetPrototype());
4060 if (proto->IsJSObject() &&
4061 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4062 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4063 }
4064 return Heap::false_value();
4065}
4066
4067
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004068static Object* Runtime_HasLocalProperty(Arguments args) {
4069 NoHandleAllocation ha;
4070 ASSERT(args.length() == 2);
4071 CONVERT_CHECKED(String, key, args[1]);
4072
ager@chromium.org9085a012009-05-11 19:22:57 +00004073 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004074 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004075 if (obj->IsJSObject()) {
4076 JSObject* object = JSObject::cast(obj);
4077 // Fast case - no interceptors.
4078 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4079 // Slow case. Either it's not there or we have an interceptor. We should
4080 // have handles for this kind of deal.
4081 HandleScope scope;
4082 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4083 Handle<String>(key));
4084 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004085 // Well, there is one exception: Handle [] on strings.
4086 uint32_t index;
4087 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004088 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004089 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004090 return Heap::true_value();
4091 }
4092 }
4093 return Heap::false_value();
4094}
4095
4096
4097static Object* Runtime_HasProperty(Arguments args) {
4098 NoHandleAllocation na;
4099 ASSERT(args.length() == 2);
4100
4101 // Only JS objects can have properties.
4102 if (args[0]->IsJSObject()) {
4103 JSObject* object = JSObject::cast(args[0]);
4104 CONVERT_CHECKED(String, key, args[1]);
4105 if (object->HasProperty(key)) return Heap::true_value();
4106 }
4107 return Heap::false_value();
4108}
4109
4110
4111static Object* Runtime_HasElement(Arguments args) {
4112 NoHandleAllocation na;
4113 ASSERT(args.length() == 2);
4114
4115 // Only JS objects can have elements.
4116 if (args[0]->IsJSObject()) {
4117 JSObject* object = JSObject::cast(args[0]);
4118 CONVERT_CHECKED(Smi, index_obj, args[1]);
4119 uint32_t index = index_obj->value();
4120 if (object->HasElement(index)) return Heap::true_value();
4121 }
4122 return Heap::false_value();
4123}
4124
4125
4126static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4127 NoHandleAllocation ha;
4128 ASSERT(args.length() == 2);
4129
4130 CONVERT_CHECKED(JSObject, object, args[0]);
4131 CONVERT_CHECKED(String, key, args[1]);
4132
4133 uint32_t index;
4134 if (key->AsArrayIndex(&index)) {
4135 return Heap::ToBoolean(object->HasElement(index));
4136 }
4137
ager@chromium.org870a0b62008-11-04 11:43:05 +00004138 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4139 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004140}
4141
4142
4143static Object* Runtime_GetPropertyNames(Arguments args) {
4144 HandleScope scope;
4145 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004146 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004147 return *GetKeysFor(object);
4148}
4149
4150
4151// Returns either a FixedArray as Runtime_GetPropertyNames,
4152// or, if the given object has an enum cache that contains
4153// all enumerable properties of the object and its prototypes
4154// have none, the map of the object. This is used to speed up
4155// the check for deletions during a for-in.
4156static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4157 ASSERT(args.length() == 1);
4158
4159 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4160
4161 if (raw_object->IsSimpleEnum()) return raw_object->map();
4162
4163 HandleScope scope;
4164 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004165 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4166 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004167
4168 // Test again, since cache may have been built by preceding call.
4169 if (object->IsSimpleEnum()) return object->map();
4170
4171 return *content;
4172}
4173
4174
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004175// Find the length of the prototype chain that is to to handled as one. If a
4176// prototype object is hidden it is to be viewed as part of the the object it
4177// is prototype for.
4178static int LocalPrototypeChainLength(JSObject* obj) {
4179 int count = 1;
4180 Object* proto = obj->GetPrototype();
4181 while (proto->IsJSObject() &&
4182 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4183 count++;
4184 proto = JSObject::cast(proto)->GetPrototype();
4185 }
4186 return count;
4187}
4188
4189
4190// Return the names of the local named properties.
4191// args[0]: object
4192static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4193 HandleScope scope;
4194 ASSERT(args.length() == 1);
4195 if (!args[0]->IsJSObject()) {
4196 return Heap::undefined_value();
4197 }
4198 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4199
4200 // Skip the global proxy as it has no properties and always delegates to the
4201 // real global object.
4202 if (obj->IsJSGlobalProxy()) {
4203 // Only collect names if access is permitted.
4204 if (obj->IsAccessCheckNeeded() &&
4205 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4206 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4207 return *Factory::NewJSArray(0);
4208 }
4209 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4210 }
4211
4212 // Find the number of objects making up this.
4213 int length = LocalPrototypeChainLength(*obj);
4214
4215 // Find the number of local properties for each of the objects.
4216 int* local_property_count = NewArray<int>(length);
4217 int total_property_count = 0;
4218 Handle<JSObject> jsproto = obj;
4219 for (int i = 0; i < length; i++) {
4220 // Only collect names if access is permitted.
4221 if (jsproto->IsAccessCheckNeeded() &&
4222 !Top::MayNamedAccess(*jsproto,
4223 Heap::undefined_value(),
4224 v8::ACCESS_KEYS)) {
4225 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4226 return *Factory::NewJSArray(0);
4227 }
4228 int n;
4229 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4230 local_property_count[i] = n;
4231 total_property_count += n;
4232 if (i < length - 1) {
4233 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4234 }
4235 }
4236
4237 // Allocate an array with storage for all the property names.
4238 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4239
4240 // Get the property names.
4241 jsproto = obj;
4242 int proto_with_hidden_properties = 0;
4243 for (int i = 0; i < length; i++) {
4244 jsproto->GetLocalPropertyNames(*names,
4245 i == 0 ? 0 : local_property_count[i - 1]);
4246 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4247 proto_with_hidden_properties++;
4248 }
4249 if (i < length - 1) {
4250 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4251 }
4252 }
4253
4254 // Filter out name of hidden propeties object.
4255 if (proto_with_hidden_properties > 0) {
4256 Handle<FixedArray> old_names = names;
4257 names = Factory::NewFixedArray(
4258 names->length() - proto_with_hidden_properties);
4259 int dest_pos = 0;
4260 for (int i = 0; i < total_property_count; i++) {
4261 Object* name = old_names->get(i);
4262 if (name == Heap::hidden_symbol()) {
4263 continue;
4264 }
4265 names->set(dest_pos++, name);
4266 }
4267 }
4268
4269 DeleteArray(local_property_count);
4270 return *Factory::NewJSArrayWithElements(names);
4271}
4272
4273
4274// Return the names of the local indexed properties.
4275// args[0]: object
4276static Object* Runtime_GetLocalElementNames(Arguments args) {
4277 HandleScope scope;
4278 ASSERT(args.length() == 1);
4279 if (!args[0]->IsJSObject()) {
4280 return Heap::undefined_value();
4281 }
4282 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4283
4284 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4285 Handle<FixedArray> names = Factory::NewFixedArray(n);
4286 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4287 return *Factory::NewJSArrayWithElements(names);
4288}
4289
4290
4291// Return information on whether an object has a named or indexed interceptor.
4292// args[0]: object
4293static Object* Runtime_GetInterceptorInfo(Arguments args) {
4294 HandleScope scope;
4295 ASSERT(args.length() == 1);
4296 if (!args[0]->IsJSObject()) {
4297 return Smi::FromInt(0);
4298 }
4299 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4300
4301 int result = 0;
4302 if (obj->HasNamedInterceptor()) result |= 2;
4303 if (obj->HasIndexedInterceptor()) result |= 1;
4304
4305 return Smi::FromInt(result);
4306}
4307
4308
4309// Return property names from named interceptor.
4310// args[0]: object
4311static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4312 HandleScope scope;
4313 ASSERT(args.length() == 1);
4314 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4315
4316 if (obj->HasNamedInterceptor()) {
4317 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4318 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4319 }
4320 return Heap::undefined_value();
4321}
4322
4323
4324// Return element names from indexed interceptor.
4325// args[0]: object
4326static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4327 HandleScope scope;
4328 ASSERT(args.length() == 1);
4329 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4330
4331 if (obj->HasIndexedInterceptor()) {
4332 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4333 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4334 }
4335 return Heap::undefined_value();
4336}
4337
4338
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004339static Object* Runtime_LocalKeys(Arguments args) {
4340 ASSERT_EQ(args.length(), 1);
4341 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4342 HandleScope scope;
4343 Handle<JSObject> object(raw_object);
4344 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4345 LOCAL_ONLY);
4346 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4347 // property array and since the result is mutable we have to create
4348 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004349 int length = contents->length();
4350 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4351 for (int i = 0; i < length; i++) {
4352 Object* entry = contents->get(i);
4353 if (entry->IsString()) {
4354 copy->set(i, entry);
4355 } else {
4356 ASSERT(entry->IsNumber());
4357 HandleScope scope;
4358 Handle<Object> entry_handle(entry);
4359 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4360 copy->set(i, *entry_str);
4361 }
4362 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004363 return *Factory::NewJSArrayWithElements(copy);
4364}
4365
4366
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004367static Object* Runtime_GetArgumentsProperty(Arguments args) {
4368 NoHandleAllocation ha;
4369 ASSERT(args.length() == 1);
4370
4371 // Compute the frame holding the arguments.
4372 JavaScriptFrameIterator it;
4373 it.AdvanceToArgumentsFrame();
4374 JavaScriptFrame* frame = it.frame();
4375
4376 // Get the actual number of provided arguments.
4377 const uint32_t n = frame->GetProvidedParametersCount();
4378
4379 // Try to convert the key to an index. If successful and within
4380 // index return the the argument from the frame.
4381 uint32_t index;
4382 if (Array::IndexFromObject(args[0], &index) && index < n) {
4383 return frame->GetParameter(index);
4384 }
4385
4386 // Convert the key to a string.
4387 HandleScope scope;
4388 bool exception = false;
4389 Handle<Object> converted =
4390 Execution::ToString(args.at<Object>(0), &exception);
4391 if (exception) return Failure::Exception();
4392 Handle<String> key = Handle<String>::cast(converted);
4393
4394 // Try to convert the string key into an array index.
4395 if (key->AsArrayIndex(&index)) {
4396 if (index < n) {
4397 return frame->GetParameter(index);
4398 } else {
4399 return Top::initial_object_prototype()->GetElement(index);
4400 }
4401 }
4402
4403 // Handle special arguments properties.
4404 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4405 if (key->Equals(Heap::callee_symbol())) return frame->function();
4406
4407 // Lookup in the initial Object.prototype object.
4408 return Top::initial_object_prototype()->GetProperty(*key);
4409}
4410
4411
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004412static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004413 HandleScope scope;
4414
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004415 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004416 Handle<Object> object = args.at<Object>(0);
4417 if (object->IsJSObject()) {
4418 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004419 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4420 js_object->TransformToFastProperties(0);
4421 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004422 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004423 return *object;
4424}
4425
4426
4427static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004428 HandleScope scope;
4429
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004430 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004431 Handle<Object> object = args.at<Object>(0);
4432 if (object->IsJSObject()) {
4433 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004434 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004435 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004436 return *object;
4437}
4438
4439
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004440static Object* Runtime_ToBool(Arguments args) {
4441 NoHandleAllocation ha;
4442 ASSERT(args.length() == 1);
4443
4444 return args[0]->ToBoolean();
4445}
4446
4447
4448// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4449// Possible optimizations: put the type string into the oddballs.
4450static Object* Runtime_Typeof(Arguments args) {
4451 NoHandleAllocation ha;
4452
4453 Object* obj = args[0];
4454 if (obj->IsNumber()) return Heap::number_symbol();
4455 HeapObject* heap_obj = HeapObject::cast(obj);
4456
4457 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004458 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004459
4460 InstanceType instance_type = heap_obj->map()->instance_type();
4461 if (instance_type < FIRST_NONSTRING_TYPE) {
4462 return Heap::string_symbol();
4463 }
4464
4465 switch (instance_type) {
4466 case ODDBALL_TYPE:
4467 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4468 return Heap::boolean_symbol();
4469 }
4470 if (heap_obj->IsNull()) {
4471 return Heap::object_symbol();
4472 }
4473 ASSERT(heap_obj->IsUndefined());
4474 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004475 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004476 return Heap::function_symbol();
4477 default:
4478 // For any kind of object not handled above, the spec rule for
4479 // host objects gives that it is okay to return "object"
4480 return Heap::object_symbol();
4481 }
4482}
4483
4484
lrn@chromium.org25156de2010-04-06 13:10:27 +00004485static bool AreDigits(const char*s, int from, int to) {
4486 for (int i = from; i < to; i++) {
4487 if (s[i] < '0' || s[i] > '9') return false;
4488 }
4489
4490 return true;
4491}
4492
4493
4494static int ParseDecimalInteger(const char*s, int from, int to) {
4495 ASSERT(to - from < 10); // Overflow is not possible.
4496 ASSERT(from < to);
4497 int d = s[from] - '0';
4498
4499 for (int i = from + 1; i < to; i++) {
4500 d = 10 * d + (s[i] - '0');
4501 }
4502
4503 return d;
4504}
4505
4506
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004507static Object* Runtime_StringToNumber(Arguments args) {
4508 NoHandleAllocation ha;
4509 ASSERT(args.length() == 1);
4510 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004511 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004512
4513 // Fast case: short integer or some sorts of junk values.
4514 int len = subject->length();
4515 if (subject->IsSeqAsciiString()) {
4516 if (len == 0) return Smi::FromInt(0);
4517
4518 char const* data = SeqAsciiString::cast(subject)->GetChars();
4519 bool minus = (data[0] == '-');
4520 int start_pos = (minus ? 1 : 0);
4521
4522 if (start_pos == len) {
4523 return Heap::nan_value();
4524 } else if (data[start_pos] > '9') {
4525 // Fast check for a junk value. A valid string may start from a
4526 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4527 // the 'I' character ('Infinity'). All of that have codes not greater than
4528 // '9' except 'I'.
4529 if (data[start_pos] != 'I') {
4530 return Heap::nan_value();
4531 }
4532 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4533 // The maximal/minimal smi has 10 digits. If the string has less digits we
4534 // know it will fit into the smi-data type.
4535 int d = ParseDecimalInteger(data, start_pos, len);
4536 if (minus) {
4537 if (d == 0) return Heap::minus_zero_value();
4538 d = -d;
4539 }
4540 return Smi::FromInt(d);
4541 }
4542 }
4543
4544 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004545 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4546}
4547
4548
4549static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4550 NoHandleAllocation ha;
4551 ASSERT(args.length() == 1);
4552
4553 CONVERT_CHECKED(JSArray, codes, args[0]);
4554 int length = Smi::cast(codes->length())->value();
4555
4556 // Check if the string can be ASCII.
4557 int i;
4558 for (i = 0; i < length; i++) {
4559 Object* element = codes->GetElement(i);
4560 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4561 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4562 break;
4563 }
4564
4565 Object* object = NULL;
4566 if (i == length) { // The string is ASCII.
4567 object = Heap::AllocateRawAsciiString(length);
4568 } else { // The string is not ASCII.
4569 object = Heap::AllocateRawTwoByteString(length);
4570 }
4571
4572 if (object->IsFailure()) return object;
4573 String* result = String::cast(object);
4574 for (int i = 0; i < length; i++) {
4575 Object* element = codes->GetElement(i);
4576 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004577 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004578 }
4579 return result;
4580}
4581
4582
4583// kNotEscaped is generated by the following:
4584//
4585// #!/bin/perl
4586// for (my $i = 0; $i < 256; $i++) {
4587// print "\n" if $i % 16 == 0;
4588// my $c = chr($i);
4589// my $escaped = 1;
4590// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4591// print $escaped ? "0, " : "1, ";
4592// }
4593
4594
4595static bool IsNotEscaped(uint16_t character) {
4596 // Only for 8 bit characters, the rest are always escaped (in a different way)
4597 ASSERT(character < 256);
4598 static const char kNotEscaped[256] = {
4599 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4600 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4601 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4602 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4603 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4604 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4605 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4606 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4607 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4608 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4609 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4610 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4611 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4612 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4613 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4614 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4615 };
4616 return kNotEscaped[character] != 0;
4617}
4618
4619
4620static Object* Runtime_URIEscape(Arguments args) {
4621 const char hex_chars[] = "0123456789ABCDEF";
4622 NoHandleAllocation ha;
4623 ASSERT(args.length() == 1);
4624 CONVERT_CHECKED(String, source, args[0]);
4625
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004626 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004627
4628 int escaped_length = 0;
4629 int length = source->length();
4630 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004631 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004632 buffer->Reset(source);
4633 while (buffer->has_more()) {
4634 uint16_t character = buffer->GetNext();
4635 if (character >= 256) {
4636 escaped_length += 6;
4637 } else if (IsNotEscaped(character)) {
4638 escaped_length++;
4639 } else {
4640 escaped_length += 3;
4641 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004642 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004643 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004644 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004645 Top::context()->mark_out_of_memory();
4646 return Failure::OutOfMemoryException();
4647 }
4648 }
4649 }
4650 // No length change implies no change. Return original string if no change.
4651 if (escaped_length == length) {
4652 return source;
4653 }
4654 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4655 if (o->IsFailure()) return o;
4656 String* destination = String::cast(o);
4657 int dest_position = 0;
4658
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004659 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004660 buffer->Rewind();
4661 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004662 uint16_t chr = buffer->GetNext();
4663 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004664 destination->Set(dest_position, '%');
4665 destination->Set(dest_position+1, 'u');
4666 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4667 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4668 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4669 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004670 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004671 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004672 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004673 dest_position++;
4674 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004675 destination->Set(dest_position, '%');
4676 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4677 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004678 dest_position += 3;
4679 }
4680 }
4681 return destination;
4682}
4683
4684
4685static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4686 static const signed char kHexValue['g'] = {
4687 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4688 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4689 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4690 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4691 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4692 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4693 -1, 10, 11, 12, 13, 14, 15 };
4694
4695 if (character1 > 'f') return -1;
4696 int hi = kHexValue[character1];
4697 if (hi == -1) return -1;
4698 if (character2 > 'f') return -1;
4699 int lo = kHexValue[character2];
4700 if (lo == -1) return -1;
4701 return (hi << 4) + lo;
4702}
4703
4704
ager@chromium.org870a0b62008-11-04 11:43:05 +00004705static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004706 int i,
4707 int length,
4708 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004709 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004710 int32_t hi = 0;
4711 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712 if (character == '%' &&
4713 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004714 source->Get(i + 1) == 'u' &&
4715 (hi = TwoDigitHex(source->Get(i + 2),
4716 source->Get(i + 3))) != -1 &&
4717 (lo = TwoDigitHex(source->Get(i + 4),
4718 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004719 *step = 6;
4720 return (hi << 8) + lo;
4721 } else if (character == '%' &&
4722 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004723 (lo = TwoDigitHex(source->Get(i + 1),
4724 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004725 *step = 3;
4726 return lo;
4727 } else {
4728 *step = 1;
4729 return character;
4730 }
4731}
4732
4733
4734static Object* Runtime_URIUnescape(Arguments args) {
4735 NoHandleAllocation ha;
4736 ASSERT(args.length() == 1);
4737 CONVERT_CHECKED(String, source, args[0]);
4738
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004739 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004740
4741 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004742 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004743
4744 int unescaped_length = 0;
4745 for (int i = 0; i < length; unescaped_length++) {
4746 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004747 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004748 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004749 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004750 i += step;
4751 }
4752
4753 // No length change implies no change. Return original string if no change.
4754 if (unescaped_length == length)
4755 return source;
4756
4757 Object* o = ascii ?
4758 Heap::AllocateRawAsciiString(unescaped_length) :
4759 Heap::AllocateRawTwoByteString(unescaped_length);
4760 if (o->IsFailure()) return o;
4761 String* destination = String::cast(o);
4762
4763 int dest_position = 0;
4764 for (int i = 0; i < length; dest_position++) {
4765 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004766 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004767 i += step;
4768 }
4769 return destination;
4770}
4771
4772
4773static Object* Runtime_StringParseInt(Arguments args) {
4774 NoHandleAllocation ha;
4775
4776 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004777 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004778
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004779 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004780
lrn@chromium.org25156de2010-04-06 13:10:27 +00004781 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4782 double value = StringToInt(s, radix);
4783 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784 return Heap::nan_value();
4785}
4786
4787
4788static Object* Runtime_StringParseFloat(Arguments args) {
4789 NoHandleAllocation ha;
4790 CONVERT_CHECKED(String, str, args[0]);
4791
4792 // ECMA-262 section 15.1.2.3, empty string is NaN
4793 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4794
4795 // Create a number object from the value.
4796 return Heap::NumberFromDouble(value);
4797}
4798
4799
4800static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4801static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4802
4803
4804template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004805static Object* ConvertCaseHelper(String* s,
4806 int length,
4807 int input_string_length,
4808 unibrow::Mapping<Converter, 128>* mapping) {
4809 // We try this twice, once with the assumption that the result is no longer
4810 // than the input and, if that assumption breaks, again with the exact
4811 // length. This may not be pretty, but it is nicer than what was here before
4812 // and I hereby claim my vaffel-is.
4813 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004814 // Allocate the resulting string.
4815 //
4816 // NOTE: This assumes that the upper/lower case of an ascii
4817 // character is also ascii. This is currently the case, but it
4818 // might break in the future if we implement more context and locale
4819 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004820 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004821 ? Heap::AllocateRawAsciiString(length)
4822 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004823 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004824 String* result = String::cast(o);
4825 bool has_changed_character = false;
4826
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004827 // Convert all characters to upper case, assuming that they will fit
4828 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004829 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004830 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004831 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004832 // We can assume that the string is not empty
4833 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004834 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004835 bool has_next = buffer->has_more();
4836 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004837 int char_length = mapping->get(current, next, chars);
4838 if (char_length == 0) {
4839 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004840 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004841 i++;
4842 } else if (char_length == 1) {
4843 // Common case: converting the letter resulted in one character.
4844 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004845 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004846 has_changed_character = true;
4847 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004848 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004849 // We've assumed that the result would be as long as the
4850 // input but here is a character that converts to several
4851 // characters. No matter, we calculate the exact length
4852 // of the result and try the whole thing again.
4853 //
4854 // Note that this leaves room for optimization. We could just
4855 // memcpy what we already have to the result string. Also,
4856 // the result string is the last object allocated we could
4857 // "realloc" it and probably, in the vast majority of cases,
4858 // extend the existing string to be able to hold the full
4859 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004860 int next_length = 0;
4861 if (has_next) {
4862 next_length = mapping->get(next, 0, chars);
4863 if (next_length == 0) next_length = 1;
4864 }
4865 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004866 while (buffer->has_more()) {
4867 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004868 // NOTE: we use 0 as the next character here because, while
4869 // the next character may affect what a character converts to,
4870 // it does not in any case affect the length of what it convert
4871 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004872 int char_length = mapping->get(current, 0, chars);
4873 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004874 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004875 if (current_length > Smi::kMaxValue) {
4876 Top::context()->mark_out_of_memory();
4877 return Failure::OutOfMemoryException();
4878 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004879 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004880 // Try again with the real length.
4881 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004882 } else {
4883 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004884 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004885 i++;
4886 }
4887 has_changed_character = true;
4888 }
4889 current = next;
4890 }
4891 if (has_changed_character) {
4892 return result;
4893 } else {
4894 // If we didn't actually change anything in doing the conversion
4895 // we simple return the result and let the converted string
4896 // become garbage; there is no reason to keep two identical strings
4897 // alive.
4898 return s;
4899 }
4900}
4901
4902
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004903static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4904 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4905 if (s->IsConsString()) {
4906 ASSERT(ConsString::cast(s)->second()->length() == 0);
4907 return SeqAsciiString::cast(ConsString::cast(s)->first());
4908 }
4909 return SeqAsciiString::cast(s);
4910}
4911
4912
4913namespace {
4914
4915struct ToLowerTraits {
4916 typedef unibrow::ToLowercase UnibrowConverter;
4917
4918 static bool ConvertAscii(char* dst, char* src, int length) {
4919 bool changed = false;
4920 for (int i = 0; i < length; ++i) {
4921 char c = src[i];
4922 if ('A' <= c && c <= 'Z') {
4923 c += ('a' - 'A');
4924 changed = true;
4925 }
4926 dst[i] = c;
4927 }
4928 return changed;
4929 }
4930};
4931
4932
4933struct ToUpperTraits {
4934 typedef unibrow::ToUppercase UnibrowConverter;
4935
4936 static bool ConvertAscii(char* dst, char* src, int length) {
4937 bool changed = false;
4938 for (int i = 0; i < length; ++i) {
4939 char c = src[i];
4940 if ('a' <= c && c <= 'z') {
4941 c -= ('a' - 'A');
4942 changed = true;
4943 }
4944 dst[i] = c;
4945 }
4946 return changed;
4947 }
4948};
4949
4950} // namespace
4951
4952
4953template <typename ConvertTraits>
4954static Object* ConvertCase(
4955 Arguments args,
4956 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004957 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004958 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004959 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004960
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004961 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004962 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004963 if (length == 0) return s;
4964
4965 // Simpler handling of ascii strings.
4966 //
4967 // NOTE: This assumes that the upper/lower case of an ascii
4968 // character is also ascii. This is currently the case, but it
4969 // might break in the future if we implement more context and locale
4970 // dependent upper/lower conversions.
4971 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4972 if (seq_ascii != NULL) {
4973 Object* o = Heap::AllocateRawAsciiString(length);
4974 if (o->IsFailure()) return o;
4975 SeqAsciiString* result = SeqAsciiString::cast(o);
4976 bool has_changed_character = ConvertTraits::ConvertAscii(
4977 result->GetChars(), seq_ascii->GetChars(), length);
4978 return has_changed_character ? result : s;
4979 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004980
4981 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4982 if (answer->IsSmi()) {
4983 // Retry with correct length.
4984 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4985 }
4986 return answer; // This may be a failure.
4987}
4988
4989
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004990static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004991 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004992}
4993
4994
4995static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004996 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004997}
4998
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004999
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005000static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5001 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5002}
5003
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005004
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005005static Object* Runtime_StringTrim(Arguments args) {
5006 NoHandleAllocation ha;
5007 ASSERT(args.length() == 3);
5008
5009 CONVERT_CHECKED(String, s, args[0]);
5010 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5011 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5012
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005013 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005014 int length = s->length();
5015
5016 int left = 0;
5017 if (trimLeft) {
5018 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5019 left++;
5020 }
5021 }
5022
5023 int right = length;
5024 if (trimRight) {
5025 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5026 right--;
5027 }
5028 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005029 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005030}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005031
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005032
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005033template <typename schar, typename pchar>
5034void FindStringIndices(Vector<const schar> subject,
5035 Vector<const pchar> pattern,
5036 ZoneList<int>* indices,
5037 unsigned int limit) {
5038 ASSERT(limit > 0);
5039 // Collect indices of pattern in subject, and the end-of-string index.
5040 // Stop after finding at most limit values.
5041 StringSearchStrategy strategy =
5042 InitializeStringSearch(pattern, sizeof(schar) == 1);
5043 switch (strategy) {
5044 case SEARCH_FAIL: return;
5045 case SEARCH_SHORT: {
5046 int pattern_length = pattern.length();
5047 int index = 0;
5048 while (limit > 0) {
5049 index = SimpleIndexOf(subject, pattern, index);
5050 if (index < 0) return;
5051 indices->Add(index);
5052 index += pattern_length;
5053 limit--;
5054 }
5055 return;
5056 }
5057 case SEARCH_LONG: {
5058 int pattern_length = pattern.length();
5059 int index = 0;
5060 while (limit > 0) {
5061 index = ComplexIndexOf(subject, pattern, index);
5062 if (index < 0) return;
5063 indices->Add(index);
5064 index += pattern_length;
5065 limit--;
5066 }
5067 return;
5068 }
5069 default:
5070 UNREACHABLE();
5071 return;
5072 }
5073}
5074
5075template <typename schar>
5076inline void FindCharIndices(Vector<const schar> subject,
5077 const schar pattern_char,
5078 ZoneList<int>* indices,
5079 unsigned int limit) {
5080 // Collect indices of pattern_char in subject, and the end-of-string index.
5081 // Stop after finding at most limit values.
5082 int index = 0;
5083 while (limit > 0) {
5084 index = SingleCharIndexOf(subject, pattern_char, index);
5085 if (index < 0) return;
5086 indices->Add(index);
5087 index++;
5088 limit--;
5089 }
5090}
5091
5092
5093static Object* Runtime_StringSplit(Arguments args) {
5094 ASSERT(args.length() == 3);
5095 HandleScope handle_scope;
5096 CONVERT_ARG_CHECKED(String, subject, 0);
5097 CONVERT_ARG_CHECKED(String, pattern, 1);
5098 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5099
5100 int subject_length = subject->length();
5101 int pattern_length = pattern->length();
5102 RUNTIME_ASSERT(pattern_length > 0);
5103
5104 // The limit can be very large (0xffffffffu), but since the pattern
5105 // isn't empty, we can never create more parts than ~half the length
5106 // of the subject.
5107
5108 if (!subject->IsFlat()) FlattenString(subject);
5109
5110 static const int kMaxInitialListCapacity = 16;
5111
5112 ZoneScope scope(DELETE_ON_EXIT);
5113
5114 // Find (up to limit) indices of separator and end-of-string in subject
5115 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5116 ZoneList<int> indices(initial_capacity);
5117 if (pattern_length == 1) {
5118 // Special case, go directly to fast single-character split.
5119 AssertNoAllocation nogc;
5120 uc16 pattern_char = pattern->Get(0);
5121 if (subject->IsTwoByteRepresentation()) {
5122 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5123 &indices,
5124 limit);
5125 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5126 FindCharIndices(subject->ToAsciiVector(),
5127 static_cast<char>(pattern_char),
5128 &indices,
5129 limit);
5130 }
5131 } else {
5132 if (!pattern->IsFlat()) FlattenString(pattern);
5133 AssertNoAllocation nogc;
5134 if (subject->IsAsciiRepresentation()) {
5135 Vector<const char> subject_vector = subject->ToAsciiVector();
5136 if (pattern->IsAsciiRepresentation()) {
5137 FindStringIndices(subject_vector,
5138 pattern->ToAsciiVector(),
5139 &indices,
5140 limit);
5141 } else {
5142 FindStringIndices(subject_vector,
5143 pattern->ToUC16Vector(),
5144 &indices,
5145 limit);
5146 }
5147 } else {
5148 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5149 if (pattern->IsAsciiRepresentation()) {
5150 FindStringIndices(subject_vector,
5151 pattern->ToAsciiVector(),
5152 &indices,
5153 limit);
5154 } else {
5155 FindStringIndices(subject_vector,
5156 pattern->ToUC16Vector(),
5157 &indices,
5158 limit);
5159 }
5160 }
5161 }
5162 if (static_cast<uint32_t>(indices.length()) < limit) {
5163 indices.Add(subject_length);
5164 }
5165 // The list indices now contains the end of each part to create.
5166
5167
5168 // Create JSArray of substrings separated by separator.
5169 int part_count = indices.length();
5170
5171 Handle<JSArray> result = Factory::NewJSArray(part_count);
5172 result->set_length(Smi::FromInt(part_count));
5173
5174 ASSERT(result->HasFastElements());
5175
5176 if (part_count == 1 && indices.at(0) == subject_length) {
5177 FixedArray::cast(result->elements())->set(0, *subject);
5178 return *result;
5179 }
5180
5181 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5182 int part_start = 0;
5183 for (int i = 0; i < part_count; i++) {
5184 HandleScope local_loop_handle;
5185 int part_end = indices.at(i);
5186 Handle<String> substring =
5187 Factory::NewSubString(subject, part_start, part_end);
5188 elements->set(i, *substring);
5189 part_start = part_end + pattern_length;
5190 }
5191
5192 return *result;
5193}
5194
5195
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005196// Copies ascii characters to the given fixed array looking up
5197// one-char strings in the cache. Gives up on the first char that is
5198// not in the cache and fills the remainder with smi zeros. Returns
5199// the length of the successfully copied prefix.
5200static int CopyCachedAsciiCharsToArray(const char* chars,
5201 FixedArray* elements,
5202 int length) {
5203 AssertNoAllocation nogc;
5204 FixedArray* ascii_cache = Heap::single_character_string_cache();
5205 Object* undefined = Heap::undefined_value();
5206 int i;
5207 for (i = 0; i < length; ++i) {
5208 Object* value = ascii_cache->get(chars[i]);
5209 if (value == undefined) break;
5210 ASSERT(!Heap::InNewSpace(value));
5211 elements->set(i, value, SKIP_WRITE_BARRIER);
5212 }
5213 if (i < length) {
5214 ASSERT(Smi::FromInt(0) == 0);
5215 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5216 }
5217#ifdef DEBUG
5218 for (int j = 0; j < length; ++j) {
5219 Object* element = elements->get(j);
5220 ASSERT(element == Smi::FromInt(0) ||
5221 (element->IsString() && String::cast(element)->LooksValid()));
5222 }
5223#endif
5224 return i;
5225}
5226
5227
5228// Converts a String to JSArray.
5229// For example, "foo" => ["f", "o", "o"].
5230static Object* Runtime_StringToArray(Arguments args) {
5231 HandleScope scope;
5232 ASSERT(args.length() == 1);
5233 CONVERT_ARG_CHECKED(String, s, 0);
5234
5235 s->TryFlatten();
5236 const int length = s->length();
5237
5238 Handle<FixedArray> elements;
5239 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5240 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5241 if (obj->IsFailure()) return obj;
5242 elements = Handle<FixedArray>(FixedArray::cast(obj));
5243
5244 Vector<const char> chars = s->ToAsciiVector();
5245 // Note, this will initialize all elements (not only the prefix)
5246 // to prevent GC from seeing partially initialized array.
5247 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5248 *elements,
5249 length);
5250
5251 for (int i = num_copied_from_cache; i < length; ++i) {
5252 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5253 }
5254 } else {
5255 elements = Factory::NewFixedArray(length);
5256 for (int i = 0; i < length; ++i) {
5257 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5258 }
5259 }
5260
5261#ifdef DEBUG
5262 for (int i = 0; i < length; ++i) {
5263 ASSERT(String::cast(elements->get(i))->length() == 1);
5264 }
5265#endif
5266
5267 return *Factory::NewJSArrayWithElements(elements);
5268}
5269
5270
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005271bool Runtime::IsUpperCaseChar(uint16_t ch) {
5272 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5273 int char_length = to_upper_mapping.get(ch, 0, chars);
5274 return char_length == 0;
5275}
5276
5277
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005278static Object* Runtime_NumberToString(Arguments args) {
5279 NoHandleAllocation ha;
5280 ASSERT(args.length() == 1);
5281
5282 Object* number = args[0];
5283 RUNTIME_ASSERT(number->IsNumber());
5284
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005285 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005286}
5287
5288
ager@chromium.org357bf652010-04-12 11:30:10 +00005289static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5290 NoHandleAllocation ha;
5291 ASSERT(args.length() == 1);
5292
5293 Object* number = args[0];
5294 RUNTIME_ASSERT(number->IsNumber());
5295
5296 return Heap::NumberToString(number, false);
5297}
5298
5299
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005300static Object* Runtime_NumberToInteger(Arguments args) {
5301 NoHandleAllocation ha;
5302 ASSERT(args.length() == 1);
5303
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005304 CONVERT_DOUBLE_CHECKED(number, args[0]);
5305
5306 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5307 if (number > 0 && number <= Smi::kMaxValue) {
5308 return Smi::FromInt(static_cast<int>(number));
5309 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005310 return Heap::NumberFromDouble(DoubleToInteger(number));
5311}
5312
5313
5314static Object* Runtime_NumberToJSUint32(Arguments args) {
5315 NoHandleAllocation ha;
5316 ASSERT(args.length() == 1);
5317
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005318 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005319 return Heap::NumberFromUint32(number);
5320}
5321
5322
5323static Object* Runtime_NumberToJSInt32(Arguments args) {
5324 NoHandleAllocation ha;
5325 ASSERT(args.length() == 1);
5326
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005327 CONVERT_DOUBLE_CHECKED(number, args[0]);
5328
5329 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5330 if (number > 0 && number <= Smi::kMaxValue) {
5331 return Smi::FromInt(static_cast<int>(number));
5332 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005333 return Heap::NumberFromInt32(DoubleToInt32(number));
5334}
5335
5336
ager@chromium.org870a0b62008-11-04 11:43:05 +00005337// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5338// a small integer.
5339static Object* Runtime_NumberToSmi(Arguments args) {
5340 NoHandleAllocation ha;
5341 ASSERT(args.length() == 1);
5342
5343 Object* obj = args[0];
5344 if (obj->IsSmi()) {
5345 return obj;
5346 }
5347 if (obj->IsHeapNumber()) {
5348 double value = HeapNumber::cast(obj)->value();
5349 int int_value = FastD2I(value);
5350 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5351 return Smi::FromInt(int_value);
5352 }
5353 }
5354 return Heap::nan_value();
5355}
5356
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005357
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005358static Object* Runtime_NumberAdd(Arguments args) {
5359 NoHandleAllocation ha;
5360 ASSERT(args.length() == 2);
5361
5362 CONVERT_DOUBLE_CHECKED(x, args[0]);
5363 CONVERT_DOUBLE_CHECKED(y, args[1]);
5364 return Heap::AllocateHeapNumber(x + y);
5365}
5366
5367
5368static Object* Runtime_NumberSub(Arguments args) {
5369 NoHandleAllocation ha;
5370 ASSERT(args.length() == 2);
5371
5372 CONVERT_DOUBLE_CHECKED(x, args[0]);
5373 CONVERT_DOUBLE_CHECKED(y, args[1]);
5374 return Heap::AllocateHeapNumber(x - y);
5375}
5376
5377
5378static Object* Runtime_NumberMul(Arguments args) {
5379 NoHandleAllocation ha;
5380 ASSERT(args.length() == 2);
5381
5382 CONVERT_DOUBLE_CHECKED(x, args[0]);
5383 CONVERT_DOUBLE_CHECKED(y, args[1]);
5384 return Heap::AllocateHeapNumber(x * y);
5385}
5386
5387
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005388static Object* Runtime_NumberUnaryMinus(Arguments args) {
5389 NoHandleAllocation ha;
5390 ASSERT(args.length() == 1);
5391
5392 CONVERT_DOUBLE_CHECKED(x, args[0]);
5393 return Heap::AllocateHeapNumber(-x);
5394}
5395
5396
5397static Object* Runtime_NumberDiv(Arguments args) {
5398 NoHandleAllocation ha;
5399 ASSERT(args.length() == 2);
5400
5401 CONVERT_DOUBLE_CHECKED(x, args[0]);
5402 CONVERT_DOUBLE_CHECKED(y, args[1]);
5403 return Heap::NewNumberFromDouble(x / y);
5404}
5405
5406
5407static Object* Runtime_NumberMod(Arguments args) {
5408 NoHandleAllocation ha;
5409 ASSERT(args.length() == 2);
5410
5411 CONVERT_DOUBLE_CHECKED(x, args[0]);
5412 CONVERT_DOUBLE_CHECKED(y, args[1]);
5413
ager@chromium.org3811b432009-10-28 14:53:37 +00005414 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005415 // NewNumberFromDouble may return a Smi instead of a Number object
5416 return Heap::NewNumberFromDouble(x);
5417}
5418
5419
5420static Object* Runtime_StringAdd(Arguments args) {
5421 NoHandleAllocation ha;
5422 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005423 CONVERT_CHECKED(String, str1, args[0]);
5424 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005425 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005426 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005427}
5428
5429
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005430template<typename sinkchar>
5431static inline void StringBuilderConcatHelper(String* special,
5432 sinkchar* sink,
5433 FixedArray* fixed_array,
5434 int array_length) {
5435 int position = 0;
5436 for (int i = 0; i < array_length; i++) {
5437 Object* element = fixed_array->get(i);
5438 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005439 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005440 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005441 int pos;
5442 int len;
5443 if (encoded_slice > 0) {
5444 // Position and length encoded in one smi.
5445 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5446 len = StringBuilderSubstringLength::decode(encoded_slice);
5447 } else {
5448 // Position and length encoded in two smis.
5449 Object* obj = fixed_array->get(++i);
5450 ASSERT(obj->IsSmi());
5451 pos = Smi::cast(obj)->value();
5452 len = -encoded_slice;
5453 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005454 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005455 sink + position,
5456 pos,
5457 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005458 position += len;
5459 } else {
5460 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005461 int element_length = string->length();
5462 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005463 position += element_length;
5464 }
5465 }
5466}
5467
5468
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005469static Object* Runtime_StringBuilderConcat(Arguments args) {
5470 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005471 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005472 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005473 if (!args[1]->IsSmi()) {
5474 Top::context()->mark_out_of_memory();
5475 return Failure::OutOfMemoryException();
5476 }
5477 int array_length = Smi::cast(args[1])->value();
5478 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005479
5480 // This assumption is used by the slice encoding in one or two smis.
5481 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5482
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005483 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005484 if (!array->HasFastElements()) {
5485 return Top::Throw(Heap::illegal_argument_symbol());
5486 }
5487 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005488 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005489 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005490 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005491
5492 if (array_length == 0) {
5493 return Heap::empty_string();
5494 } else if (array_length == 1) {
5495 Object* first = fixed_array->get(0);
5496 if (first->IsString()) return first;
5497 }
5498
ager@chromium.org5ec48922009-05-05 07:25:34 +00005499 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005500 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005501 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502 for (int i = 0; i < array_length; i++) {
5503 Object* elt = fixed_array->get(i);
5504 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005505 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005506 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005507 if (len > 0) {
5508 // Position and length encoded in one smi.
5509 int pos = len >> 11;
5510 len &= 0x7ff;
5511 if (pos + len > special_length) {
5512 return Top::Throw(Heap::illegal_argument_symbol());
5513 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005514 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005515 } else {
5516 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005517 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005518 // Get the position and check that it is also a smi.
5519 i++;
5520 if (i >= array_length) {
5521 return Top::Throw(Heap::illegal_argument_symbol());
5522 }
5523 Object* pos = fixed_array->get(i);
5524 if (!pos->IsSmi()) {
5525 return Top::Throw(Heap::illegal_argument_symbol());
5526 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005527 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005528 } else if (elt->IsString()) {
5529 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005530 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005531 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005532 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005533 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005534 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005535 } else {
5536 return Top::Throw(Heap::illegal_argument_symbol());
5537 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005538 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005539 Top::context()->mark_out_of_memory();
5540 return Failure::OutOfMemoryException();
5541 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005542 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005543 }
5544
5545 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005546 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548 if (ascii) {
5549 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005550 if (object->IsFailure()) return object;
5551 SeqAsciiString* answer = SeqAsciiString::cast(object);
5552 StringBuilderConcatHelper(special,
5553 answer->GetChars(),
5554 fixed_array,
5555 array_length);
5556 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005557 } else {
5558 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005559 if (object->IsFailure()) return object;
5560 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5561 StringBuilderConcatHelper(special,
5562 answer->GetChars(),
5563 fixed_array,
5564 array_length);
5565 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005566 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005567}
5568
5569
5570static Object* Runtime_NumberOr(Arguments args) {
5571 NoHandleAllocation ha;
5572 ASSERT(args.length() == 2);
5573
5574 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5575 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5576 return Heap::NumberFromInt32(x | y);
5577}
5578
5579
5580static Object* Runtime_NumberAnd(Arguments args) {
5581 NoHandleAllocation ha;
5582 ASSERT(args.length() == 2);
5583
5584 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5585 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5586 return Heap::NumberFromInt32(x & y);
5587}
5588
5589
5590static Object* Runtime_NumberXor(Arguments args) {
5591 NoHandleAllocation ha;
5592 ASSERT(args.length() == 2);
5593
5594 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5595 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5596 return Heap::NumberFromInt32(x ^ y);
5597}
5598
5599
5600static Object* Runtime_NumberNot(Arguments args) {
5601 NoHandleAllocation ha;
5602 ASSERT(args.length() == 1);
5603
5604 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5605 return Heap::NumberFromInt32(~x);
5606}
5607
5608
5609static Object* Runtime_NumberShl(Arguments args) {
5610 NoHandleAllocation ha;
5611 ASSERT(args.length() == 2);
5612
5613 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5614 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5615 return Heap::NumberFromInt32(x << (y & 0x1f));
5616}
5617
5618
5619static Object* Runtime_NumberShr(Arguments args) {
5620 NoHandleAllocation ha;
5621 ASSERT(args.length() == 2);
5622
5623 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5624 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5625 return Heap::NumberFromUint32(x >> (y & 0x1f));
5626}
5627
5628
5629static Object* Runtime_NumberSar(Arguments args) {
5630 NoHandleAllocation ha;
5631 ASSERT(args.length() == 2);
5632
5633 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5634 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5635 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5636}
5637
5638
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005639static Object* Runtime_NumberEquals(Arguments args) {
5640 NoHandleAllocation ha;
5641 ASSERT(args.length() == 2);
5642
5643 CONVERT_DOUBLE_CHECKED(x, args[0]);
5644 CONVERT_DOUBLE_CHECKED(y, args[1]);
5645 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5646 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5647 if (x == y) return Smi::FromInt(EQUAL);
5648 Object* result;
5649 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5650 result = Smi::FromInt(EQUAL);
5651 } else {
5652 result = Smi::FromInt(NOT_EQUAL);
5653 }
5654 return result;
5655}
5656
5657
5658static Object* Runtime_StringEquals(Arguments args) {
5659 NoHandleAllocation ha;
5660 ASSERT(args.length() == 2);
5661
5662 CONVERT_CHECKED(String, x, args[0]);
5663 CONVERT_CHECKED(String, y, args[1]);
5664
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005665 bool not_equal = !x->Equals(y);
5666 // This is slightly convoluted because the value that signifies
5667 // equality is 0 and inequality is 1 so we have to negate the result
5668 // from String::Equals.
5669 ASSERT(not_equal == 0 || not_equal == 1);
5670 STATIC_CHECK(EQUAL == 0);
5671 STATIC_CHECK(NOT_EQUAL == 1);
5672 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005673}
5674
5675
5676static Object* Runtime_NumberCompare(Arguments args) {
5677 NoHandleAllocation ha;
5678 ASSERT(args.length() == 3);
5679
5680 CONVERT_DOUBLE_CHECKED(x, args[0]);
5681 CONVERT_DOUBLE_CHECKED(y, args[1]);
5682 if (isnan(x) || isnan(y)) return args[2];
5683 if (x == y) return Smi::FromInt(EQUAL);
5684 if (isless(x, y)) return Smi::FromInt(LESS);
5685 return Smi::FromInt(GREATER);
5686}
5687
5688
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005689// Compare two Smis as if they were converted to strings and then
5690// compared lexicographically.
5691static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5692 NoHandleAllocation ha;
5693 ASSERT(args.length() == 2);
5694
5695 // Arrays for the individual characters of the two Smis. Smis are
5696 // 31 bit integers and 10 decimal digits are therefore enough.
5697 static int x_elms[10];
5698 static int y_elms[10];
5699
5700 // Extract the integer values from the Smis.
5701 CONVERT_CHECKED(Smi, x, args[0]);
5702 CONVERT_CHECKED(Smi, y, args[1]);
5703 int x_value = x->value();
5704 int y_value = y->value();
5705
5706 // If the integers are equal so are the string representations.
5707 if (x_value == y_value) return Smi::FromInt(EQUAL);
5708
5709 // If one of the integers are zero the normal integer order is the
5710 // same as the lexicographic order of the string representations.
5711 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5712
ager@chromium.org32912102009-01-16 10:38:43 +00005713 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005714 // smallest because the char code of '-' is less than the char code
5715 // of any digit. Otherwise, we make both values positive.
5716 if (x_value < 0 || y_value < 0) {
5717 if (y_value >= 0) return Smi::FromInt(LESS);
5718 if (x_value >= 0) return Smi::FromInt(GREATER);
5719 x_value = -x_value;
5720 y_value = -y_value;
5721 }
5722
5723 // Convert the integers to arrays of their decimal digits.
5724 int x_index = 0;
5725 int y_index = 0;
5726 while (x_value > 0) {
5727 x_elms[x_index++] = x_value % 10;
5728 x_value /= 10;
5729 }
5730 while (y_value > 0) {
5731 y_elms[y_index++] = y_value % 10;
5732 y_value /= 10;
5733 }
5734
5735 // Loop through the arrays of decimal digits finding the first place
5736 // where they differ.
5737 while (--x_index >= 0 && --y_index >= 0) {
5738 int diff = x_elms[x_index] - y_elms[y_index];
5739 if (diff != 0) return Smi::FromInt(diff);
5740 }
5741
5742 // If one array is a suffix of the other array, the longest array is
5743 // the representation of the largest of the Smis in the
5744 // lexicographic ordering.
5745 return Smi::FromInt(x_index - y_index);
5746}
5747
5748
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005749static Object* StringInputBufferCompare(String* x, String* y) {
5750 static StringInputBuffer bufx;
5751 static StringInputBuffer bufy;
5752 bufx.Reset(x);
5753 bufy.Reset(y);
5754 while (bufx.has_more() && bufy.has_more()) {
5755 int d = bufx.GetNext() - bufy.GetNext();
5756 if (d < 0) return Smi::FromInt(LESS);
5757 else if (d > 0) return Smi::FromInt(GREATER);
5758 }
5759
5760 // x is (non-trivial) prefix of y:
5761 if (bufy.has_more()) return Smi::FromInt(LESS);
5762 // y is prefix of x:
5763 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5764}
5765
5766
5767static Object* FlatStringCompare(String* x, String* y) {
5768 ASSERT(x->IsFlat());
5769 ASSERT(y->IsFlat());
5770 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5771 int prefix_length = x->length();
5772 if (y->length() < prefix_length) {
5773 prefix_length = y->length();
5774 equal_prefix_result = Smi::FromInt(GREATER);
5775 } else if (y->length() > prefix_length) {
5776 equal_prefix_result = Smi::FromInt(LESS);
5777 }
5778 int r;
5779 if (x->IsAsciiRepresentation()) {
5780 Vector<const char> x_chars = x->ToAsciiVector();
5781 if (y->IsAsciiRepresentation()) {
5782 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005783 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005784 } else {
5785 Vector<const uc16> y_chars = y->ToUC16Vector();
5786 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5787 }
5788 } else {
5789 Vector<const uc16> x_chars = x->ToUC16Vector();
5790 if (y->IsAsciiRepresentation()) {
5791 Vector<const char> y_chars = y->ToAsciiVector();
5792 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5793 } else {
5794 Vector<const uc16> y_chars = y->ToUC16Vector();
5795 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5796 }
5797 }
5798 Object* result;
5799 if (r == 0) {
5800 result = equal_prefix_result;
5801 } else {
5802 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5803 }
5804 ASSERT(result == StringInputBufferCompare(x, y));
5805 return result;
5806}
5807
5808
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005809static Object* Runtime_StringCompare(Arguments args) {
5810 NoHandleAllocation ha;
5811 ASSERT(args.length() == 2);
5812
5813 CONVERT_CHECKED(String, x, args[0]);
5814 CONVERT_CHECKED(String, y, args[1]);
5815
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005816 Counters::string_compare_runtime.Increment();
5817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005818 // A few fast case tests before we flatten.
5819 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005820 if (y->length() == 0) {
5821 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005822 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005823 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005824 return Smi::FromInt(LESS);
5825 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005826
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005827 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005828 if (d < 0) return Smi::FromInt(LESS);
5829 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005830
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005831 Object* obj = Heap::PrepareForCompare(x);
5832 if (obj->IsFailure()) return obj;
5833 obj = Heap::PrepareForCompare(y);
5834 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005835
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005836 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5837 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005838}
5839
5840
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005841static Object* Runtime_Math_acos(Arguments args) {
5842 NoHandleAllocation ha;
5843 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005844 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005845
5846 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005847 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005848}
5849
5850
5851static Object* Runtime_Math_asin(Arguments args) {
5852 NoHandleAllocation ha;
5853 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005854 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005855
5856 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005857 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005858}
5859
5860
5861static Object* Runtime_Math_atan(Arguments args) {
5862 NoHandleAllocation ha;
5863 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005864 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005865
5866 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005867 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005868}
5869
5870
5871static Object* Runtime_Math_atan2(Arguments args) {
5872 NoHandleAllocation ha;
5873 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005874 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005875
5876 CONVERT_DOUBLE_CHECKED(x, args[0]);
5877 CONVERT_DOUBLE_CHECKED(y, args[1]);
5878 double result;
5879 if (isinf(x) && isinf(y)) {
5880 // Make sure that the result in case of two infinite arguments
5881 // is a multiple of Pi / 4. The sign of the result is determined
5882 // by the first argument (x) and the sign of the second argument
5883 // determines the multiplier: one or three.
5884 static double kPiDividedBy4 = 0.78539816339744830962;
5885 int multiplier = (x < 0) ? -1 : 1;
5886 if (y < 0) multiplier *= 3;
5887 result = multiplier * kPiDividedBy4;
5888 } else {
5889 result = atan2(x, y);
5890 }
5891 return Heap::AllocateHeapNumber(result);
5892}
5893
5894
5895static Object* Runtime_Math_ceil(Arguments args) {
5896 NoHandleAllocation ha;
5897 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005898 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005899
5900 CONVERT_DOUBLE_CHECKED(x, args[0]);
5901 return Heap::NumberFromDouble(ceiling(x));
5902}
5903
5904
5905static Object* Runtime_Math_cos(Arguments args) {
5906 NoHandleAllocation ha;
5907 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005908 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005909
5910 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005911 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005912}
5913
5914
5915static Object* Runtime_Math_exp(Arguments args) {
5916 NoHandleAllocation ha;
5917 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005918 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005919
5920 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005921 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005922}
5923
5924
5925static Object* Runtime_Math_floor(Arguments args) {
5926 NoHandleAllocation ha;
5927 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005928 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005929
5930 CONVERT_DOUBLE_CHECKED(x, args[0]);
5931 return Heap::NumberFromDouble(floor(x));
5932}
5933
5934
5935static Object* Runtime_Math_log(Arguments args) {
5936 NoHandleAllocation ha;
5937 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005938 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005939
5940 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005941 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005942}
5943
5944
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005945// Helper function to compute x^y, where y is known to be an
5946// integer. Uses binary decomposition to limit the number of
5947// multiplications; see the discussion in "Hacker's Delight" by Henry
5948// S. Warren, Jr., figure 11-6, page 213.
5949static double powi(double x, int y) {
5950 ASSERT(y != kMinInt);
5951 unsigned n = (y < 0) ? -y : y;
5952 double m = x;
5953 double p = 1;
5954 while (true) {
5955 if ((n & 1) != 0) p *= m;
5956 n >>= 1;
5957 if (n == 0) {
5958 if (y < 0) {
5959 // Unfortunately, we have to be careful when p has reached
5960 // infinity in the computation, because sometimes the higher
5961 // internal precision in the pow() implementation would have
5962 // given us a finite p. This happens very rarely.
5963 double result = 1.0 / p;
5964 return (result == 0 && isinf(p))
5965 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
5966 : result;
5967 } else {
5968 return p;
5969 }
5970 }
5971 m *= m;
5972 }
5973}
5974
5975
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005976static Object* Runtime_Math_pow(Arguments args) {
5977 NoHandleAllocation ha;
5978 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005979 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005980
5981 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005982
5983 // If the second argument is a smi, it is much faster to call the
5984 // custom powi() function than the generic pow().
5985 if (args[1]->IsSmi()) {
5986 int y = Smi::cast(args[1])->value();
5987 return Heap::AllocateHeapNumber(powi(x, y));
5988 }
5989
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005990 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005991
5992 if (!isinf(x)) {
5993 if (y == 0.5) {
5994 // It's not uncommon to use Math.pow(x, 0.5) to compute the
5995 // square root of a number. To speed up such computations, we
5996 // explictly check for this case and use the sqrt() function
5997 // which is faster than pow().
5998 return Heap::AllocateHeapNumber(sqrt(x));
5999 } else if (y == -0.5) {
6000 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6001 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6002 }
6003 }
6004
6005 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006006 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006007 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6008 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006009 } else {
6010 return Heap::AllocateHeapNumber(pow(x, y));
6011 }
6012}
6013
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006014// Fast version of Math.pow if we know that y is not an integer and
6015// y is not -0.5 or 0.5. Used as slowcase from codegen.
6016static Object* Runtime_Math_pow_cfunction(Arguments args) {
6017 NoHandleAllocation ha;
6018 ASSERT(args.length() == 2);
6019 CONVERT_DOUBLE_CHECKED(x, args[0]);
6020 CONVERT_DOUBLE_CHECKED(y, args[1]);
6021 if (y == 0) {
6022 return Smi::FromInt(1);
6023 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6024 return Heap::nan_value();
6025 } else {
6026 return Heap::AllocateHeapNumber(pow(x, y));
6027 }
6028}
6029
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006030
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006031static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006032 NoHandleAllocation ha;
6033 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006034 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006035
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006036 if (!args[0]->IsHeapNumber()) {
6037 // Must be smi. Return the argument unchanged for all the other types
6038 // to make fuzz-natives test happy.
6039 return args[0];
6040 }
6041
6042 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6043
6044 double value = number->value();
6045 int exponent = number->get_exponent();
6046 int sign = number->get_sign();
6047
6048 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6049 // should be rounded to 2^30, which is not smi.
6050 if (!sign && exponent <= kSmiValueSize - 3) {
6051 return Smi::FromInt(static_cast<int>(value + 0.5));
6052 }
6053
6054 // If the magnitude is big enough, there's no place for fraction part. If we
6055 // try to add 0.5 to this number, 1.0 will be added instead.
6056 if (exponent >= 52) {
6057 return number;
6058 }
6059
6060 if (sign && value >= -0.5) return Heap::minus_zero_value();
6061
6062 return Heap::NumberFromDouble(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006063}
6064
6065
6066static Object* Runtime_Math_sin(Arguments args) {
6067 NoHandleAllocation ha;
6068 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006069 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006070
6071 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006072 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006073}
6074
6075
6076static Object* Runtime_Math_sqrt(Arguments args) {
6077 NoHandleAllocation ha;
6078 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006079 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006080
6081 CONVERT_DOUBLE_CHECKED(x, args[0]);
6082 return Heap::AllocateHeapNumber(sqrt(x));
6083}
6084
6085
6086static Object* Runtime_Math_tan(Arguments args) {
6087 NoHandleAllocation ha;
6088 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006089 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006090
6091 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006092 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006093}
6094
6095
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006096static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006097 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6098 181, 212, 243, 273, 304, 334};
6099 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6100 182, 213, 244, 274, 305, 335};
6101
6102 year += month / 12;
6103 month %= 12;
6104 if (month < 0) {
6105 year--;
6106 month += 12;
6107 }
6108
6109 ASSERT(month >= 0);
6110 ASSERT(month < 12);
6111
6112 // year_delta is an arbitrary number such that:
6113 // a) year_delta = -1 (mod 400)
6114 // b) year + year_delta > 0 for years in the range defined by
6115 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6116 // Jan 1 1970. This is required so that we don't run into integer
6117 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006118 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006119 // operations.
6120 static const int year_delta = 399999;
6121 static const int base_day = 365 * (1970 + year_delta) +
6122 (1970 + year_delta) / 4 -
6123 (1970 + year_delta) / 100 +
6124 (1970 + year_delta) / 400;
6125
6126 int year1 = year + year_delta;
6127 int day_from_year = 365 * year1 +
6128 year1 / 4 -
6129 year1 / 100 +
6130 year1 / 400 -
6131 base_day;
6132
6133 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006134 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006135 }
6136
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006137 return day_from_year + day_from_month_leap[month] + day - 1;
6138}
6139
6140
6141static Object* Runtime_DateMakeDay(Arguments args) {
6142 NoHandleAllocation ha;
6143 ASSERT(args.length() == 3);
6144
6145 CONVERT_SMI_CHECKED(year, args[0]);
6146 CONVERT_SMI_CHECKED(month, args[1]);
6147 CONVERT_SMI_CHECKED(date, args[2]);
6148
6149 return Smi::FromInt(MakeDay(year, month, date));
6150}
6151
6152
6153static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6154static const int kDaysIn4Years = 4 * 365 + 1;
6155static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6156static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6157static const int kDays1970to2000 = 30 * 365 + 7;
6158static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6159 kDays1970to2000;
6160static const int kYearsOffset = 400000;
6161
6162static const char kDayInYear[] = {
6163 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6164 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6165 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6166 22, 23, 24, 25, 26, 27, 28,
6167 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6168 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6169 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6170 22, 23, 24, 25, 26, 27, 28, 29, 30,
6171 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6172 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6173 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6174 22, 23, 24, 25, 26, 27, 28, 29, 30,
6175 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6176 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6177 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6178 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6179 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6180 22, 23, 24, 25, 26, 27, 28, 29, 30,
6181 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6182 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6183 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6184 22, 23, 24, 25, 26, 27, 28, 29, 30,
6185 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6186 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6187
6188 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6189 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6190 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6191 22, 23, 24, 25, 26, 27, 28,
6192 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6193 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6194 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6195 22, 23, 24, 25, 26, 27, 28, 29, 30,
6196 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6197 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6198 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6199 22, 23, 24, 25, 26, 27, 28, 29, 30,
6200 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6201 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6202 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6203 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6204 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6205 22, 23, 24, 25, 26, 27, 28, 29, 30,
6206 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6207 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6208 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6209 22, 23, 24, 25, 26, 27, 28, 29, 30,
6210 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6211 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6212
6213 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6214 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6215 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6216 22, 23, 24, 25, 26, 27, 28, 29,
6217 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6218 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6219 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6220 22, 23, 24, 25, 26, 27, 28, 29, 30,
6221 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6222 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6223 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6224 22, 23, 24, 25, 26, 27, 28, 29, 30,
6225 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6226 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6227 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6228 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 29, 30, 31,
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,
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, 31,
6237
6238 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6239 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6240 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6241 22, 23, 24, 25, 26, 27, 28,
6242 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6243 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6244 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6245 22, 23, 24, 25, 26, 27, 28, 29, 30,
6246 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6247 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6248 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6249 22, 23, 24, 25, 26, 27, 28, 29, 30,
6250 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6251 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6252 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6253 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
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,
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, 29, 30, 31,
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,
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, 31};
6262
6263static const char kMonthInYear[] = {
6264 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,
6265 0, 0, 0, 0, 0, 0,
6266 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,
6267 1, 1, 1,
6268 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,
6269 2, 2, 2, 2, 2, 2,
6270 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,
6271 3, 3, 3, 3, 3,
6272 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,
6273 4, 4, 4, 4, 4, 4,
6274 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,
6275 5, 5, 5, 5, 5,
6276 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,
6277 6, 6, 6, 6, 6, 6,
6278 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,
6279 7, 7, 7, 7, 7, 7,
6280 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,
6281 8, 8, 8, 8, 8,
6282 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,
6283 9, 9, 9, 9, 9, 9,
6284 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6285 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6286 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6287 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6288
6289 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,
6290 0, 0, 0, 0, 0, 0,
6291 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,
6292 1, 1, 1,
6293 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,
6294 2, 2, 2, 2, 2, 2,
6295 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,
6296 3, 3, 3, 3, 3,
6297 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,
6298 4, 4, 4, 4, 4, 4,
6299 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,
6300 5, 5, 5, 5, 5,
6301 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,
6302 6, 6, 6, 6, 6, 6,
6303 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,
6304 7, 7, 7, 7, 7, 7,
6305 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,
6306 8, 8, 8, 8, 8,
6307 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,
6308 9, 9, 9, 9, 9, 9,
6309 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6310 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6311 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6312 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6313
6314 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,
6315 0, 0, 0, 0, 0, 0,
6316 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,
6317 1, 1, 1, 1,
6318 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,
6319 2, 2, 2, 2, 2, 2,
6320 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,
6321 3, 3, 3, 3, 3,
6322 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,
6323 4, 4, 4, 4, 4, 4,
6324 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,
6325 5, 5, 5, 5, 5,
6326 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,
6327 6, 6, 6, 6, 6, 6,
6328 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,
6329 7, 7, 7, 7, 7, 7,
6330 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,
6331 8, 8, 8, 8, 8,
6332 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,
6333 9, 9, 9, 9, 9, 9,
6334 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6335 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6336 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6337 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6338
6339 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,
6340 0, 0, 0, 0, 0, 0,
6341 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,
6342 1, 1, 1,
6343 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,
6344 2, 2, 2, 2, 2, 2,
6345 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,
6346 3, 3, 3, 3, 3,
6347 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,
6348 4, 4, 4, 4, 4, 4,
6349 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,
6350 5, 5, 5, 5, 5,
6351 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,
6352 6, 6, 6, 6, 6, 6,
6353 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,
6354 7, 7, 7, 7, 7, 7,
6355 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,
6356 8, 8, 8, 8, 8,
6357 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,
6358 9, 9, 9, 9, 9, 9,
6359 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6360 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6361 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6362 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6363
6364
6365// This function works for dates from 1970 to 2099.
6366static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006367 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006368#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006369 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006370#endif
6371
6372 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6373 date %= kDaysIn4Years;
6374
6375 month = kMonthInYear[date];
6376 day = kDayInYear[date];
6377
6378 ASSERT(MakeDay(year, month, day) == save_date);
6379}
6380
6381
6382static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006383 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006384#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006385 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006386#endif
6387
6388 date += kDaysOffset;
6389 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6390 date %= kDaysIn400Years;
6391
6392 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6393
6394 date--;
6395 int yd1 = date / kDaysIn100Years;
6396 date %= kDaysIn100Years;
6397 year += 100 * yd1;
6398
6399 date++;
6400 int yd2 = date / kDaysIn4Years;
6401 date %= kDaysIn4Years;
6402 year += 4 * yd2;
6403
6404 date--;
6405 int yd3 = date / 365;
6406 date %= 365;
6407 year += yd3;
6408
6409 bool is_leap = (!yd1 || yd2) && !yd3;
6410
6411 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006412 ASSERT(is_leap || (date >= 0));
6413 ASSERT((date < 365) || (is_leap && (date < 366)));
6414 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6415 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6416 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006417
6418 if (is_leap) {
6419 day = kDayInYear[2*365 + 1 + date];
6420 month = kMonthInYear[2*365 + 1 + date];
6421 } else {
6422 day = kDayInYear[date];
6423 month = kMonthInYear[date];
6424 }
6425
6426 ASSERT(MakeDay(year, month, day) == save_date);
6427}
6428
6429
6430static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006431 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006432 if (date >= 0 && date < 32 * kDaysIn4Years) {
6433 DateYMDFromTimeAfter1970(date, year, month, day);
6434 } else {
6435 DateYMDFromTimeSlow(date, year, month, day);
6436 }
6437}
6438
6439
6440static Object* Runtime_DateYMDFromTime(Arguments args) {
6441 NoHandleAllocation ha;
6442 ASSERT(args.length() == 2);
6443
6444 CONVERT_DOUBLE_CHECKED(t, args[0]);
6445 CONVERT_CHECKED(JSArray, res_array, args[1]);
6446
6447 int year, month, day;
6448 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6449
6450 res_array->SetElement(0, Smi::FromInt(year));
6451 res_array->SetElement(1, Smi::FromInt(month));
6452 res_array->SetElement(2, Smi::FromInt(day));
6453
6454 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006455}
6456
6457
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006458static Object* Runtime_NewArgumentsFast(Arguments args) {
6459 NoHandleAllocation ha;
6460 ASSERT(args.length() == 3);
6461
6462 JSFunction* callee = JSFunction::cast(args[0]);
6463 Object** parameters = reinterpret_cast<Object**>(args[1]);
6464 const int length = Smi::cast(args[2])->value();
6465
6466 Object* result = Heap::AllocateArgumentsObject(callee, length);
6467 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006468 // Allocate the elements if needed.
6469 if (length > 0) {
6470 // Allocate the fixed array.
6471 Object* obj = Heap::AllocateRawFixedArray(length);
6472 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006473
6474 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006475 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
6476 FixedArray* array = FixedArray::cast(obj);
6477 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006478
6479 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006480 for (int i = 0; i < length; i++) {
6481 array->set(i, *--parameters, mode);
6482 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006483 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006484 }
6485 return result;
6486}
6487
6488
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006489static Object* Runtime_NewClosure(Arguments args) {
6490 HandleScope scope;
6491 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006492 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006493 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006494
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006495 PretenureFlag pretenure = (context->global_context() == *context)
6496 ? TENURED // Allocate global closures in old space.
6497 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006498 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006499 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006500 return *result;
6501}
6502
6503
ager@chromium.org5c838252010-02-19 08:53:10 +00006504static Code* ComputeConstructStub(Handle<JSFunction> function) {
6505 Handle<Object> prototype = Factory::null_value();
6506 if (function->has_instance_prototype()) {
6507 prototype = Handle<Object>(function->instance_prototype());
6508 }
6509 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006510 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006511 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006512 if (code->IsFailure()) {
6513 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6514 }
6515 return Code::cast(code);
6516 }
6517
ager@chromium.org5c838252010-02-19 08:53:10 +00006518 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006519}
6520
6521
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006522static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006523 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006524 ASSERT(args.length() == 1);
6525
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006526 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006527
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006528 // If the constructor isn't a proper function we throw a type error.
6529 if (!constructor->IsJSFunction()) {
6530 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6531 Handle<Object> type_error =
6532 Factory::NewTypeError("not_constructor", arguments);
6533 return Top::Throw(*type_error);
6534 }
6535
6536 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006537#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006538 // Handle stepping into constructors if step into is active.
6539 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006540 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006541 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006542#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006543
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006544 if (function->has_initial_map()) {
6545 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006546 // The 'Function' function ignores the receiver object when
6547 // called using 'new' and creates a new JSFunction object that
6548 // is returned. The receiver object is only used for error
6549 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006550 // JSFunction. Factory::NewJSObject() should not be used to
6551 // allocate JSFunctions since it does not properly initialize
6552 // the shared part of the function. Since the receiver is
6553 // ignored anyway, we use the global object as the receiver
6554 // instead of a new JSFunction object. This way, errors are
6555 // reported the same way whether or not 'Function' is called
6556 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006557 return Top::context()->global();
6558 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006559 }
6560
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006561 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006562 Handle<SharedFunctionInfo> shared(function->shared());
6563 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006564
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006565 bool first_allocation = !function->has_initial_map();
6566 Handle<JSObject> result = Factory::NewJSObject(function);
6567 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006568 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006569 ComputeConstructStub(Handle<JSFunction>(function)));
6570 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006571 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006572
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006573 Counters::constructed_objects.Increment();
6574 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006575
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006576 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006577}
6578
6579
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006580static Object* Runtime_LazyCompile(Arguments args) {
6581 HandleScope scope;
6582 ASSERT(args.length() == 1);
6583
6584 Handle<JSFunction> function = args.at<JSFunction>(0);
6585#ifdef DEBUG
6586 if (FLAG_trace_lazy) {
6587 PrintF("[lazy: ");
6588 function->shared()->name()->Print();
6589 PrintF("]\n");
6590 }
6591#endif
6592
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006593 // Compile the target function. Here we compile using CompileLazyInLoop in
6594 // order to get the optimized version. This helps code like delta-blue
6595 // that calls performance-critical routines through constructors. A
6596 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6597 // direct call. Since the in-loop tracking takes place through CallICs
6598 // this means that things called through constructors are never known to
6599 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006600 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006601 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006602 return Failure::Exception();
6603 }
6604
6605 return function->code();
6606}
6607
6608
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006609static Object* Runtime_GetFunctionDelegate(Arguments args) {
6610 HandleScope scope;
6611 ASSERT(args.length() == 1);
6612 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6613 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6614}
6615
6616
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006617static Object* Runtime_GetConstructorDelegate(Arguments args) {
6618 HandleScope scope;
6619 ASSERT(args.length() == 1);
6620 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6621 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6622}
6623
6624
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006625static Object* Runtime_NewContext(Arguments args) {
6626 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006627 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006628
kasper.lund7276f142008-07-30 08:49:36 +00006629 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006630 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6631 Object* result = Heap::AllocateFunctionContext(length, function);
6632 if (result->IsFailure()) return result;
6633
6634 Top::set_context(Context::cast(result));
6635
kasper.lund7276f142008-07-30 08:49:36 +00006636 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006637}
6638
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006639static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006640 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006641 Object* js_object = object;
6642 if (!js_object->IsJSObject()) {
6643 js_object = js_object->ToObject();
6644 if (js_object->IsFailure()) {
6645 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006646 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006647 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006648 Handle<Object> result =
6649 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6650 return Top::Throw(*result);
6651 }
6652 }
6653
6654 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006655 Heap::AllocateWithContext(Top::context(),
6656 JSObject::cast(js_object),
6657 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006658 if (result->IsFailure()) return result;
6659
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006660 Context* context = Context::cast(result);
6661 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006662
kasper.lund7276f142008-07-30 08:49:36 +00006663 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006664}
6665
6666
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006667static Object* Runtime_PushContext(Arguments args) {
6668 NoHandleAllocation ha;
6669 ASSERT(args.length() == 1);
6670 return PushContextHelper(args[0], false);
6671}
6672
6673
6674static Object* Runtime_PushCatchContext(Arguments args) {
6675 NoHandleAllocation ha;
6676 ASSERT(args.length() == 1);
6677 return PushContextHelper(args[0], true);
6678}
6679
6680
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006681static Object* Runtime_LookupContext(Arguments args) {
6682 HandleScope scope;
6683 ASSERT(args.length() == 2);
6684
6685 CONVERT_ARG_CHECKED(Context, context, 0);
6686 CONVERT_ARG_CHECKED(String, name, 1);
6687
6688 int index;
6689 PropertyAttributes attributes;
6690 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006691 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006692 context->Lookup(name, flags, &index, &attributes);
6693
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006694 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006695 ASSERT(holder->IsJSObject());
6696 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006697 }
6698
6699 // No intermediate context found. Use global object by default.
6700 return Top::context()->global();
6701}
6702
6703
ager@chromium.orga1645e22009-09-09 19:27:10 +00006704// A mechanism to return a pair of Object pointers in registers (if possible).
6705// How this is achieved is calling convention-dependent.
6706// All currently supported x86 compiles uses calling conventions that are cdecl
6707// variants where a 64-bit value is returned in two 32-bit registers
6708// (edx:eax on ia32, r1:r0 on ARM).
6709// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6710// In Win64 calling convention, a struct of two pointers is returned in memory,
6711// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006712#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006713struct ObjectPair {
6714 Object* x;
6715 Object* y;
6716};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006717
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006718static inline ObjectPair MakePair(Object* x, Object* y) {
6719 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006720 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6721 // In Win64 they are assigned to a hidden first argument.
6722 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006723}
6724#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006725typedef uint64_t ObjectPair;
6726static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006727 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006728 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006729}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006730#endif
6731
6732
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006733static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006734 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6735 USE(attributes);
6736 return x->IsTheHole() ? Heap::undefined_value() : x;
6737}
6738
6739
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006740static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6741 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006742 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006743 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006744 JSFunction* context_extension_function =
6745 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006746 // If the holder isn't a context extension object, we just return it
6747 // as the receiver. This allows arguments objects to be used as
6748 // receivers, but only if they are put in the context scope chain
6749 // explicitly via a with-statement.
6750 Object* constructor = holder->map()->constructor();
6751 if (constructor != context_extension_function) return holder;
6752 // Fall back to using the global object as the receiver if the
6753 // property turns out to be a local variable allocated in a context
6754 // extension object - introduced via eval.
6755 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006756}
6757
6758
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006759static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006760 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006761 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006762
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006763 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006764 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006765 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006766 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006767 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006768
6769 int index;
6770 PropertyAttributes attributes;
6771 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006772 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006773 context->Lookup(name, flags, &index, &attributes);
6774
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006775 // If the index is non-negative, the slot has been found in a local
6776 // variable or a parameter. Read it from the context object or the
6777 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006778 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006779 // If the "property" we were looking for is a local variable or an
6780 // argument in a context, the receiver is the global object; see
6781 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6782 JSObject* receiver = Top::context()->global()->global_receiver();
6783 Object* value = (holder->IsContext())
6784 ? Context::cast(*holder)->get(index)
6785 : JSObject::cast(*holder)->GetElement(index);
6786 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006787 }
6788
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006789 // If the holder is found, we read the property from it.
6790 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006791 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006792 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006793 JSObject* receiver;
6794 if (object->IsGlobalObject()) {
6795 receiver = GlobalObject::cast(object)->global_receiver();
6796 } else if (context->is_exception_holder(*holder)) {
6797 receiver = Top::context()->global()->global_receiver();
6798 } else {
6799 receiver = ComputeReceiverForNonGlobal(object);
6800 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006801 // No need to unhole the value here. This is taken care of by the
6802 // GetProperty function.
6803 Object* value = object->GetProperty(*name);
6804 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006805 }
6806
6807 if (throw_error) {
6808 // The property doesn't exist - throw exception.
6809 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006810 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006811 return MakePair(Top::Throw(*reference_error), NULL);
6812 } else {
6813 // The property doesn't exist - return undefined
6814 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6815 }
6816}
6817
6818
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006819static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006820 return LoadContextSlotHelper(args, true);
6821}
6822
6823
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006824static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006825 return LoadContextSlotHelper(args, false);
6826}
6827
6828
6829static Object* Runtime_StoreContextSlot(Arguments args) {
6830 HandleScope scope;
6831 ASSERT(args.length() == 3);
6832
6833 Handle<Object> value(args[0]);
6834 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006835 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006836
6837 int index;
6838 PropertyAttributes attributes;
6839 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006840 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006841 context->Lookup(name, flags, &index, &attributes);
6842
6843 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006844 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006845 // Ignore if read_only variable.
6846 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006847 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006848 }
6849 } else {
6850 ASSERT((attributes & READ_ONLY) == 0);
6851 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006852 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006853 USE(result);
6854 ASSERT(!result->IsFailure());
6855 }
6856 return *value;
6857 }
6858
6859 // Slow case: The property is not in a FixedArray context.
6860 // It is either in an JSObject extension context or it was not found.
6861 Handle<JSObject> context_ext;
6862
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006863 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006864 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006865 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006866 } else {
6867 // The property was not found. It needs to be stored in the global context.
6868 ASSERT(attributes == ABSENT);
6869 attributes = NONE;
6870 context_ext = Handle<JSObject>(Top::context()->global());
6871 }
6872
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006873 // Set the property, but ignore if read_only variable on the context
6874 // extension object itself.
6875 if ((attributes & READ_ONLY) == 0 ||
6876 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006877 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6878 if (set.is_null()) {
6879 // Failure::Exception is converted to a null handle in the
6880 // handle-based methods such as SetProperty. We therefore need
6881 // to convert null handles back to exceptions.
6882 ASSERT(Top::has_pending_exception());
6883 return Failure::Exception();
6884 }
6885 }
6886 return *value;
6887}
6888
6889
6890static Object* Runtime_Throw(Arguments args) {
6891 HandleScope scope;
6892 ASSERT(args.length() == 1);
6893
6894 return Top::Throw(args[0]);
6895}
6896
6897
6898static Object* Runtime_ReThrow(Arguments args) {
6899 HandleScope scope;
6900 ASSERT(args.length() == 1);
6901
6902 return Top::ReThrow(args[0]);
6903}
6904
6905
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006906static Object* Runtime_PromoteScheduledException(Arguments args) {
6907 ASSERT_EQ(0, args.length());
6908 return Top::PromoteScheduledException();
6909}
6910
6911
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006912static Object* Runtime_ThrowReferenceError(Arguments args) {
6913 HandleScope scope;
6914 ASSERT(args.length() == 1);
6915
6916 Handle<Object> name(args[0]);
6917 Handle<Object> reference_error =
6918 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6919 return Top::Throw(*reference_error);
6920}
6921
6922
6923static Object* Runtime_StackOverflow(Arguments args) {
6924 NoHandleAllocation na;
6925 return Top::StackOverflow();
6926}
6927
6928
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006929static Object* Runtime_StackGuard(Arguments args) {
6930 ASSERT(args.length() == 1);
6931
6932 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006933 if (StackGuard::IsStackOverflow()) {
6934 return Runtime_StackOverflow(args);
6935 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006936
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006937 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006938}
6939
6940
6941// NOTE: These PrintXXX functions are defined for all builds (not just
6942// DEBUG builds) because we may want to be able to trace function
6943// calls in all modes.
6944static void PrintString(String* str) {
6945 // not uncommon to have empty strings
6946 if (str->length() > 0) {
6947 SmartPointer<char> s =
6948 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
6949 PrintF("%s", *s);
6950 }
6951}
6952
6953
6954static void PrintObject(Object* obj) {
6955 if (obj->IsSmi()) {
6956 PrintF("%d", Smi::cast(obj)->value());
6957 } else if (obj->IsString() || obj->IsSymbol()) {
6958 PrintString(String::cast(obj));
6959 } else if (obj->IsNumber()) {
6960 PrintF("%g", obj->Number());
6961 } else if (obj->IsFailure()) {
6962 PrintF("<failure>");
6963 } else if (obj->IsUndefined()) {
6964 PrintF("<undefined>");
6965 } else if (obj->IsNull()) {
6966 PrintF("<null>");
6967 } else if (obj->IsTrue()) {
6968 PrintF("<true>");
6969 } else if (obj->IsFalse()) {
6970 PrintF("<false>");
6971 } else {
6972 PrintF("%p", obj);
6973 }
6974}
6975
6976
6977static int StackSize() {
6978 int n = 0;
6979 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
6980 return n;
6981}
6982
6983
6984static void PrintTransition(Object* result) {
6985 // indentation
6986 { const int nmax = 80;
6987 int n = StackSize();
6988 if (n <= nmax)
6989 PrintF("%4d:%*s", n, n, "");
6990 else
6991 PrintF("%4d:%*s", n, nmax, "...");
6992 }
6993
6994 if (result == NULL) {
6995 // constructor calls
6996 JavaScriptFrameIterator it;
6997 JavaScriptFrame* frame = it.frame();
6998 if (frame->IsConstructor()) PrintF("new ");
6999 // function name
7000 Object* fun = frame->function();
7001 if (fun->IsJSFunction()) {
7002 PrintObject(JSFunction::cast(fun)->shared()->name());
7003 } else {
7004 PrintObject(fun);
7005 }
7006 // function arguments
7007 // (we are intentionally only printing the actually
7008 // supplied parameters, not all parameters required)
7009 PrintF("(this=");
7010 PrintObject(frame->receiver());
7011 const int length = frame->GetProvidedParametersCount();
7012 for (int i = 0; i < length; i++) {
7013 PrintF(", ");
7014 PrintObject(frame->GetParameter(i));
7015 }
7016 PrintF(") {\n");
7017
7018 } else {
7019 // function result
7020 PrintF("} -> ");
7021 PrintObject(result);
7022 PrintF("\n");
7023 }
7024}
7025
7026
7027static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007028 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007029 NoHandleAllocation ha;
7030 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007031 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007032}
7033
7034
7035static Object* Runtime_TraceExit(Arguments args) {
7036 NoHandleAllocation ha;
7037 PrintTransition(args[0]);
7038 return args[0]; // return TOS
7039}
7040
7041
7042static Object* Runtime_DebugPrint(Arguments args) {
7043 NoHandleAllocation ha;
7044 ASSERT(args.length() == 1);
7045
7046#ifdef DEBUG
7047 if (args[0]->IsString()) {
7048 // If we have a string, assume it's a code "marker"
7049 // and print some interesting cpu debugging info.
7050 JavaScriptFrameIterator it;
7051 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007052 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7053 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054 } else {
7055 PrintF("DebugPrint: ");
7056 }
7057 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007058 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007059 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007060 HeapObject::cast(args[0])->map()->Print();
7061 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007062#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007063 // ShortPrint is available in release mode. Print is not.
7064 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007065#endif
7066 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007067 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007068
7069 return args[0]; // return TOS
7070}
7071
7072
7073static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007074 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007075 NoHandleAllocation ha;
7076 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007077 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007078}
7079
7080
mads.s.ager31e71382008-08-13 09:32:07 +00007081static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007082 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007083 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007084
7085 // According to ECMA-262, section 15.9.1, page 117, the precision of
7086 // the number in a Date object representing a particular instant in
7087 // time is milliseconds. Therefore, we floor the result of getting
7088 // the OS time.
7089 double millis = floor(OS::TimeCurrentMillis());
7090 return Heap::NumberFromDouble(millis);
7091}
7092
7093
7094static Object* Runtime_DateParseString(Arguments args) {
7095 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007096 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007097
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007098 CONVERT_ARG_CHECKED(String, str, 0);
7099 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007101 CONVERT_ARG_CHECKED(JSArray, output, 1);
7102 RUNTIME_ASSERT(output->HasFastElements());
7103
7104 AssertNoAllocation no_allocation;
7105
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007106 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007107 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7108 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007109 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007110 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007111 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007112 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007113 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7114 }
7115
7116 if (result) {
7117 return *output;
7118 } else {
7119 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007120 }
7121}
7122
7123
7124static Object* Runtime_DateLocalTimezone(Arguments args) {
7125 NoHandleAllocation ha;
7126 ASSERT(args.length() == 1);
7127
7128 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007129 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007130 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7131}
7132
7133
7134static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7135 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007136 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007137
7138 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7139}
7140
7141
7142static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7143 NoHandleAllocation ha;
7144 ASSERT(args.length() == 1);
7145
7146 CONVERT_DOUBLE_CHECKED(x, args[0]);
7147 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7148}
7149
7150
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007151static Object* Runtime_GlobalReceiver(Arguments args) {
7152 ASSERT(args.length() == 1);
7153 Object* global = args[0];
7154 if (!global->IsJSGlobalObject()) return Heap::null_value();
7155 return JSGlobalObject::cast(global)->global_receiver();
7156}
7157
7158
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007159static Object* Runtime_CompileString(Arguments args) {
7160 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007161 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007162 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007163 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007164
ager@chromium.org381abbb2009-02-25 13:23:22 +00007165 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007166 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007167 Compiler::ValidationState validate = (is_json->IsTrue())
7168 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007169 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7170 context,
7171 true,
7172 validate);
7173 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007174 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007175 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007176 return *fun;
7177}
7178
7179
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007180static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7181 ASSERT(args.length() == 3);
7182 if (!args[0]->IsJSFunction()) {
7183 return MakePair(Top::ThrowIllegalOperation(), NULL);
7184 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007185
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007186 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007187 Handle<JSFunction> callee = args.at<JSFunction>(0);
7188 Handle<Object> receiver; // Will be overwritten.
7189
7190 // Compute the calling context.
7191 Handle<Context> context = Handle<Context>(Top::context());
7192#ifdef DEBUG
7193 // Make sure Top::context() agrees with the old code that traversed
7194 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007195 StackFrameLocator locator;
7196 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007197 ASSERT(Context::cast(frame->context()) == *context);
7198#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007199
7200 // Find where the 'eval' symbol is bound. It is unaliased only if
7201 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007202 int index = -1;
7203 PropertyAttributes attributes = ABSENT;
7204 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007205 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7206 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007207 // Stop search when eval is found or when the global context is
7208 // reached.
7209 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007210 if (context->is_function_context()) {
7211 context = Handle<Context>(Context::cast(context->closure()->context()));
7212 } else {
7213 context = Handle<Context>(context->previous());
7214 }
7215 }
7216
iposva@chromium.org245aa852009-02-10 00:49:54 +00007217 // If eval could not be resolved, it has been deleted and we need to
7218 // throw a reference error.
7219 if (attributes == ABSENT) {
7220 Handle<Object> name = Factory::eval_symbol();
7221 Handle<Object> reference_error =
7222 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007223 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007224 }
7225
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007226 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007227 // 'eval' is not bound in the global context. Just call the function
7228 // with the given arguments. This is not necessarily the global eval.
7229 if (receiver->IsContext()) {
7230 context = Handle<Context>::cast(receiver);
7231 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007232 } else if (receiver->IsJSContextExtensionObject()) {
7233 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007234 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007235 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007236 }
7237
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007238 // 'eval' is bound in the global context, but it may have been overwritten.
7239 // Compare it to the builtin 'GlobalEval' function to make sure.
7240 if (*callee != Top::global_context()->global_eval_fun() ||
7241 !args[1]->IsString()) {
7242 return MakePair(*callee, Top::context()->global()->global_receiver());
7243 }
7244
7245 // Deal with a normal eval call with a string argument. Compile it
7246 // and return the compiled function bound in the local context.
7247 Handle<String> source = args.at<String>(1);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007248 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007249 source,
7250 Handle<Context>(Top::context()),
7251 Top::context()->IsGlobalContext(),
7252 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007253 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7254 callee = Factory::NewFunctionFromSharedFunctionInfo(
7255 shared,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007256 Handle<Context>(Top::context()),
7257 NOT_TENURED);
7258 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007259}
7260
7261
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007262static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7263 // This utility adjusts the property attributes for newly created Function
7264 // object ("new Function(...)") by changing the map.
7265 // All it does is changing the prototype property to enumerable
7266 // as specified in ECMA262, 15.3.5.2.
7267 HandleScope scope;
7268 ASSERT(args.length() == 1);
7269 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7270 ASSERT(func->map()->instance_type() ==
7271 Top::function_instance_map()->instance_type());
7272 ASSERT(func->map()->instance_size() ==
7273 Top::function_instance_map()->instance_size());
7274 func->set_map(*Top::function_instance_map());
7275 return *func;
7276}
7277
7278
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007279// Push an array unto an array of arrays if it is not already in the
7280// array. Returns true if the element was pushed on the stack and
7281// false otherwise.
7282static Object* Runtime_PushIfAbsent(Arguments args) {
7283 ASSERT(args.length() == 2);
7284 CONVERT_CHECKED(JSArray, array, args[0]);
7285 CONVERT_CHECKED(JSArray, element, args[1]);
7286 RUNTIME_ASSERT(array->HasFastElements());
7287 int length = Smi::cast(array->length())->value();
7288 FixedArray* elements = FixedArray::cast(array->elements());
7289 for (int i = 0; i < length; i++) {
7290 if (elements->get(i) == element) return Heap::false_value();
7291 }
7292 Object* obj = array->SetFastElement(length, element);
7293 if (obj->IsFailure()) return obj;
7294 return Heap::true_value();
7295}
7296
7297
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007298/**
7299 * A simple visitor visits every element of Array's.
7300 * The backend storage can be a fixed array for fast elements case,
7301 * or a dictionary for sparse array. Since Dictionary is a subtype
7302 * of FixedArray, the class can be used by both fast and slow cases.
7303 * The second parameter of the constructor, fast_elements, specifies
7304 * whether the storage is a FixedArray or Dictionary.
7305 *
7306 * An index limit is used to deal with the situation that a result array
7307 * length overflows 32-bit non-negative integer.
7308 */
7309class ArrayConcatVisitor {
7310 public:
7311 ArrayConcatVisitor(Handle<FixedArray> storage,
7312 uint32_t index_limit,
7313 bool fast_elements) :
7314 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007315 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007316
7317 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007318 if (i >= index_limit_ - index_offset_) return;
7319 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007320
7321 if (fast_elements_) {
7322 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7323 storage_->set(index, *elm);
7324
7325 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007326 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7327 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007328 Factory::DictionaryAtNumberPut(dict, index, elm);
7329 if (!result.is_identical_to(dict))
7330 storage_ = result;
7331 }
7332 }
7333
7334 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007335 if (index_limit_ - index_offset_ < delta) {
7336 index_offset_ = index_limit_;
7337 } else {
7338 index_offset_ += delta;
7339 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007340 }
7341
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007342 Handle<FixedArray> storage() { return storage_; }
7343
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007344 private:
7345 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007346 // Limit on the accepted indices. Elements with indices larger than the
7347 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007348 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007349 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007350 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007351 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007352};
7353
7354
ager@chromium.org3811b432009-10-28 14:53:37 +00007355template<class ExternalArrayClass, class ElementType>
7356static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7357 bool elements_are_ints,
7358 bool elements_are_guaranteed_smis,
7359 uint32_t range,
7360 ArrayConcatVisitor* visitor) {
7361 Handle<ExternalArrayClass> array(
7362 ExternalArrayClass::cast(receiver->elements()));
7363 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7364
7365 if (visitor != NULL) {
7366 if (elements_are_ints) {
7367 if (elements_are_guaranteed_smis) {
7368 for (uint32_t j = 0; j < len; j++) {
7369 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7370 visitor->visit(j, e);
7371 }
7372 } else {
7373 for (uint32_t j = 0; j < len; j++) {
7374 int64_t val = static_cast<int64_t>(array->get(j));
7375 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7376 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7377 visitor->visit(j, e);
7378 } else {
7379 Handle<Object> e(
7380 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7381 visitor->visit(j, e);
7382 }
7383 }
7384 }
7385 } else {
7386 for (uint32_t j = 0; j < len; j++) {
7387 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7388 visitor->visit(j, e);
7389 }
7390 }
7391 }
7392
7393 return len;
7394}
7395
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007396/**
7397 * A helper function that visits elements of a JSObject. Only elements
7398 * whose index between 0 and range (exclusive) are visited.
7399 *
7400 * If the third parameter, visitor, is not NULL, the visitor is called
7401 * with parameters, 'visitor_index_offset + element index' and the element.
7402 *
7403 * It returns the number of visisted elements.
7404 */
7405static uint32_t IterateElements(Handle<JSObject> receiver,
7406 uint32_t range,
7407 ArrayConcatVisitor* visitor) {
7408 uint32_t num_of_elements = 0;
7409
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007410 switch (receiver->GetElementsKind()) {
7411 case JSObject::FAST_ELEMENTS: {
7412 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7413 uint32_t len = elements->length();
7414 if (range < len) {
7415 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007416 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007417
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007418 for (uint32_t j = 0; j < len; j++) {
7419 Handle<Object> e(elements->get(j));
7420 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007421 num_of_elements++;
7422 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007423 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007424 }
7425 }
7426 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007427 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007428 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007429 case JSObject::PIXEL_ELEMENTS: {
7430 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7431 uint32_t len = pixels->length();
7432 if (range < len) {
7433 len = range;
7434 }
7435
7436 for (uint32_t j = 0; j < len; j++) {
7437 num_of_elements++;
7438 if (visitor != NULL) {
7439 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7440 visitor->visit(j, e);
7441 }
7442 }
7443 break;
7444 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007445 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7446 num_of_elements =
7447 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7448 receiver, true, true, range, visitor);
7449 break;
7450 }
7451 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7452 num_of_elements =
7453 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7454 receiver, true, true, range, visitor);
7455 break;
7456 }
7457 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7458 num_of_elements =
7459 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7460 receiver, true, true, range, visitor);
7461 break;
7462 }
7463 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7464 num_of_elements =
7465 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7466 receiver, true, true, range, visitor);
7467 break;
7468 }
7469 case JSObject::EXTERNAL_INT_ELEMENTS: {
7470 num_of_elements =
7471 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7472 receiver, true, false, range, visitor);
7473 break;
7474 }
7475 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7476 num_of_elements =
7477 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7478 receiver, true, false, range, visitor);
7479 break;
7480 }
7481 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7482 num_of_elements =
7483 IterateExternalArrayElements<ExternalFloatArray, float>(
7484 receiver, false, false, range, visitor);
7485 break;
7486 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007487 case JSObject::DICTIONARY_ELEMENTS: {
7488 Handle<NumberDictionary> dict(receiver->element_dictionary());
7489 uint32_t capacity = dict->Capacity();
7490 for (uint32_t j = 0; j < capacity; j++) {
7491 Handle<Object> k(dict->KeyAt(j));
7492 if (dict->IsKey(*k)) {
7493 ASSERT(k->IsNumber());
7494 uint32_t index = static_cast<uint32_t>(k->Number());
7495 if (index < range) {
7496 num_of_elements++;
7497 if (visitor) {
7498 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7499 }
7500 }
7501 }
7502 }
7503 break;
7504 }
7505 default:
7506 UNREACHABLE();
7507 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007508 }
7509
7510 return num_of_elements;
7511}
7512
7513
7514/**
7515 * A helper function that visits elements of an Array object, and elements
7516 * on its prototypes.
7517 *
7518 * Elements on prototypes are visited first, and only elements whose indices
7519 * less than Array length are visited.
7520 *
7521 * If a ArrayConcatVisitor object is given, the visitor is called with
7522 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007523 *
7524 * The returned number of elements is an upper bound on the actual number
7525 * of elements added. If the same element occurs in more than one object
7526 * in the array's prototype chain, it will be counted more than once, but
7527 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007528 */
7529static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7530 ArrayConcatVisitor* visitor) {
7531 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7532 Handle<Object> obj = array;
7533
7534 static const int kEstimatedPrototypes = 3;
7535 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7536
7537 // Visit prototype first. If an element on the prototype is shadowed by
7538 // the inheritor using the same index, the ArrayConcatVisitor visits
7539 // the prototype element before the shadowing element.
7540 // The visitor can simply overwrite the old value by new value using
7541 // the same index. This follows Array::concat semantics.
7542 while (!obj->IsNull()) {
7543 objects.Add(Handle<JSObject>::cast(obj));
7544 obj = Handle<Object>(obj->GetPrototype());
7545 }
7546
7547 uint32_t nof_elements = 0;
7548 for (int i = objects.length() - 1; i >= 0; i--) {
7549 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007550 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007551 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007552
7553 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7554 nof_elements = JSObject::kMaxElementCount;
7555 } else {
7556 nof_elements += encountered_elements;
7557 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007558 }
7559
7560 return nof_elements;
7561}
7562
7563
7564/**
7565 * A helper function of Runtime_ArrayConcat.
7566 *
7567 * The first argument is an Array of arrays and objects. It is the
7568 * same as the arguments array of Array::concat JS function.
7569 *
7570 * If an argument is an Array object, the function visits array
7571 * elements. If an argument is not an Array object, the function
7572 * visits the object as if it is an one-element array.
7573 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007574 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007575 * non-negative number is used as new length. For example, if one
7576 * array length is 2^32 - 1, second array length is 1, the
7577 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007578 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7579 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007580 */
7581static uint32_t IterateArguments(Handle<JSArray> arguments,
7582 ArrayConcatVisitor* visitor) {
7583 uint32_t visited_elements = 0;
7584 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7585
7586 for (uint32_t i = 0; i < num_of_args; i++) {
7587 Handle<Object> obj(arguments->GetElement(i));
7588 if (obj->IsJSArray()) {
7589 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7590 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7591 uint32_t nof_elements =
7592 IterateArrayAndPrototypeElements(array, visitor);
7593 // Total elements of array and its prototype chain can be more than
7594 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007595 // the array length number of elements. We use the length as an estimate
7596 // for the actual number of elements added.
7597 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7598 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7599 visited_elements = JSArray::kMaxElementCount;
7600 } else {
7601 visited_elements += added_elements;
7602 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007603 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007604 } else {
7605 if (visitor) {
7606 visitor->visit(0, obj);
7607 visitor->increase_index_offset(1);
7608 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007609 if (visited_elements < JSArray::kMaxElementCount) {
7610 visited_elements++;
7611 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007612 }
7613 }
7614 return visited_elements;
7615}
7616
7617
7618/**
7619 * Array::concat implementation.
7620 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007621 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7622 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007623 */
7624static Object* Runtime_ArrayConcat(Arguments args) {
7625 ASSERT(args.length() == 1);
7626 HandleScope handle_scope;
7627
7628 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7629 Handle<JSArray> arguments(arg_arrays);
7630
7631 // Pass 1: estimate the number of elements of the result
7632 // (it could be more than real numbers if prototype has elements).
7633 uint32_t result_length = 0;
7634 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7635
7636 { AssertNoAllocation nogc;
7637 for (uint32_t i = 0; i < num_of_args; i++) {
7638 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007639 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007640 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007641 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007642 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7643 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007644 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007645 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007646 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7647 result_length = JSObject::kMaxElementCount;
7648 break;
7649 }
7650 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007651 }
7652 }
7653
7654 // Allocate an empty array, will set length and content later.
7655 Handle<JSArray> result = Factory::NewJSArray(0);
7656
7657 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7658 // If estimated number of elements is more than half of length, a
7659 // fixed array (fast case) is more time and space-efficient than a
7660 // dictionary.
7661 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7662
7663 Handle<FixedArray> storage;
7664 if (fast_case) {
7665 // The backing storage array must have non-existing elements to
7666 // preserve holes across concat operations.
7667 storage = Factory::NewFixedArrayWithHoles(result_length);
7668
7669 } else {
7670 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7671 uint32_t at_least_space_for = estimate_nof_elements +
7672 (estimate_nof_elements >> 2);
7673 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007674 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007675 }
7676
7677 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7678
7679 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7680
7681 IterateArguments(arguments, &visitor);
7682
7683 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007684 // Please note the storage might have changed in the visitor.
7685 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007686
7687 return *result;
7688}
7689
7690
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007691// This will not allocate (flatten the string), but it may run
7692// very slowly for very deeply nested ConsStrings. For debugging use only.
7693static Object* Runtime_GlobalPrint(Arguments args) {
7694 NoHandleAllocation ha;
7695 ASSERT(args.length() == 1);
7696
7697 CONVERT_CHECKED(String, string, args[0]);
7698 StringInputBuffer buffer(string);
7699 while (buffer.has_more()) {
7700 uint16_t character = buffer.GetNext();
7701 PrintF("%c", character);
7702 }
7703 return string;
7704}
7705
ager@chromium.org5ec48922009-05-05 07:25:34 +00007706// Moves all own elements of an object, that are below a limit, to positions
7707// starting at zero. All undefined values are placed after non-undefined values,
7708// and are followed by non-existing element. Does not change the length
7709// property.
7710// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007711static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007712 ASSERT(args.length() == 2);
7713 CONVERT_CHECKED(JSObject, object, args[0]);
7714 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7715 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007716}
7717
7718
7719// Move contents of argument 0 (an array) to argument 1 (an array)
7720static Object* Runtime_MoveArrayContents(Arguments args) {
7721 ASSERT(args.length() == 2);
7722 CONVERT_CHECKED(JSArray, from, args[0]);
7723 CONVERT_CHECKED(JSArray, to, args[1]);
7724 to->SetContent(FixedArray::cast(from->elements()));
7725 to->set_length(from->length());
7726 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007727 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007728 return to;
7729}
7730
7731
7732// How many elements does this array have?
7733static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7734 ASSERT(args.length() == 1);
7735 CONVERT_CHECKED(JSArray, array, args[0]);
7736 HeapObject* elements = array->elements();
7737 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007738 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007739 } else {
7740 return array->length();
7741 }
7742}
7743
7744
7745// Returns an array that tells you where in the [0, length) interval an array
7746// might have elements. Can either return keys or intervals. Keys can have
7747// gaps in (undefined). Intervals can also span over some undefined keys.
7748static Object* Runtime_GetArrayKeys(Arguments args) {
7749 ASSERT(args.length() == 2);
7750 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007751 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007752 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007753 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007754 // Create an array and get all the keys into it, then remove all the
7755 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007756 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007757 int keys_length = keys->length();
7758 for (int i = 0; i < keys_length; i++) {
7759 Object* key = keys->get(i);
7760 uint32_t index;
7761 if (!Array::IndexFromObject(key, &index) || index >= length) {
7762 // Zap invalid keys.
7763 keys->set_undefined(i);
7764 }
7765 }
7766 return *Factory::NewJSArrayWithElements(keys);
7767 } else {
7768 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7769 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007770 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00007771 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
7772 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007773 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007774 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007775 single_interval->set(1, *length_object);
7776 return *Factory::NewJSArrayWithElements(single_interval);
7777 }
7778}
7779
7780
7781// DefineAccessor takes an optional final argument which is the
7782// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7783// to the way accessors are implemented, it is set for both the getter
7784// and setter on the first call to DefineAccessor and ignored on
7785// subsequent calls.
7786static Object* Runtime_DefineAccessor(Arguments args) {
7787 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7788 // Compute attributes.
7789 PropertyAttributes attributes = NONE;
7790 if (args.length() == 5) {
7791 CONVERT_CHECKED(Smi, attrs, args[4]);
7792 int value = attrs->value();
7793 // Only attribute bits should be set.
7794 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7795 attributes = static_cast<PropertyAttributes>(value);
7796 }
7797
7798 CONVERT_CHECKED(JSObject, obj, args[0]);
7799 CONVERT_CHECKED(String, name, args[1]);
7800 CONVERT_CHECKED(Smi, flag, args[2]);
7801 CONVERT_CHECKED(JSFunction, fun, args[3]);
7802 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7803}
7804
7805
7806static Object* Runtime_LookupAccessor(Arguments args) {
7807 ASSERT(args.length() == 3);
7808 CONVERT_CHECKED(JSObject, obj, args[0]);
7809 CONVERT_CHECKED(String, name, args[1]);
7810 CONVERT_CHECKED(Smi, flag, args[2]);
7811 return obj->LookupAccessor(name, flag->value() == 0);
7812}
7813
7814
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007815#ifdef ENABLE_DEBUGGER_SUPPORT
7816static Object* Runtime_DebugBreak(Arguments args) {
7817 ASSERT(args.length() == 0);
7818 return Execution::DebugBreakHelper();
7819}
7820
7821
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007822// Helper functions for wrapping and unwrapping stack frame ids.
7823static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007824 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007825 return Smi::FromInt(id >> 2);
7826}
7827
7828
7829static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7830 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7831}
7832
7833
7834// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007835// args[0]: debug event listener function to set or null or undefined for
7836// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007837// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007838static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007839 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007840 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7841 args[0]->IsUndefined() ||
7842 args[0]->IsNull());
7843 Handle<Object> callback = args.at<Object>(0);
7844 Handle<Object> data = args.at<Object>(1);
7845 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007846
7847 return Heap::undefined_value();
7848}
7849
7850
7851static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007852 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007853 StackGuard::DebugBreak();
7854 return Heap::undefined_value();
7855}
7856
7857
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007858static Object* DebugLookupResultValue(Object* receiver, String* name,
7859 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007860 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007861 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007862 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007863 case NORMAL:
7864 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007865 if (value->IsTheHole()) {
7866 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007867 }
7868 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007869 case FIELD:
7870 value =
7871 JSObject::cast(
7872 result->holder())->FastPropertyAt(result->GetFieldIndex());
7873 if (value->IsTheHole()) {
7874 return Heap::undefined_value();
7875 }
7876 return value;
7877 case CONSTANT_FUNCTION:
7878 return result->GetConstantFunction();
7879 case CALLBACKS: {
7880 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007881 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007882 value = receiver->GetPropertyWithCallback(
7883 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007884 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007885 value = Top::pending_exception();
7886 Top::clear_pending_exception();
7887 if (caught_exception != NULL) {
7888 *caught_exception = true;
7889 }
7890 }
7891 return value;
7892 } else {
7893 return Heap::undefined_value();
7894 }
7895 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007896 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007897 case MAP_TRANSITION:
7898 case CONSTANT_TRANSITION:
7899 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007900 return Heap::undefined_value();
7901 default:
7902 UNREACHABLE();
7903 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007904 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007905 return Heap::undefined_value();
7906}
7907
7908
ager@chromium.org32912102009-01-16 10:38:43 +00007909// Get debugger related details for an object property.
7910// args[0]: object holding property
7911// args[1]: name of the property
7912//
7913// The array returned contains the following information:
7914// 0: Property value
7915// 1: Property details
7916// 2: Property value is exception
7917// 3: Getter function if defined
7918// 4: Setter function if defined
7919// Items 2-4 are only filled if the property has either a getter or a setter
7920// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007921static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007922 HandleScope scope;
7923
7924 ASSERT(args.length() == 2);
7925
7926 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7927 CONVERT_ARG_CHECKED(String, name, 1);
7928
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007929 // Make sure to set the current context to the context before the debugger was
7930 // entered (if the debugger is entered). The reason for switching context here
7931 // is that for some property lookups (accessors and interceptors) callbacks
7932 // into the embedding application can occour, and the embedding application
7933 // could have the assumption that its own global context is the current
7934 // context and not some internal debugger context.
7935 SaveContext save;
7936 if (Debug::InDebugger()) {
7937 Top::set_context(*Debug::debugger_entry()->GetContext());
7938 }
7939
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007940 // Skip the global proxy as it has no properties and always delegates to the
7941 // real global object.
7942 if (obj->IsJSGlobalProxy()) {
7943 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
7944 }
7945
7946
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007947 // Check if the name is trivially convertible to an index and get the element
7948 // if so.
7949 uint32_t index;
7950 if (name->AsArrayIndex(&index)) {
7951 Handle<FixedArray> details = Factory::NewFixedArray(2);
7952 details->set(0, Runtime::GetElementOrCharAt(obj, index));
7953 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
7954 return *Factory::NewJSArrayWithElements(details);
7955 }
7956
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007957 // Find the number of objects making up this.
7958 int length = LocalPrototypeChainLength(*obj);
7959
7960 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007961 Handle<JSObject> jsproto = obj;
7962 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007963 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007964 jsproto->LocalLookup(*name, &result);
7965 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007966 // LookupResult is not GC safe as it holds raw object pointers.
7967 // GC can happen later in this code so put the required fields into
7968 // local variables using handles when required for later use.
7969 PropertyType result_type = result.type();
7970 Handle<Object> result_callback_obj;
7971 if (result_type == CALLBACKS) {
7972 result_callback_obj = Handle<Object>(result.GetCallbackObject());
7973 }
7974 Smi* property_details = result.GetPropertyDetails().AsSmi();
7975 // DebugLookupResultValue can cause GC so details from LookupResult needs
7976 // to be copied to handles before this.
7977 bool caught_exception = false;
7978 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
7979 &caught_exception);
7980 if (raw_value->IsFailure()) return raw_value;
7981 Handle<Object> value(raw_value);
7982
7983 // If the callback object is a fixed array then it contains JavaScript
7984 // getter and/or setter.
7985 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
7986 result_callback_obj->IsFixedArray();
7987 Handle<FixedArray> details =
7988 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
7989 details->set(0, *value);
7990 details->set(1, property_details);
7991 if (hasJavaScriptAccessors) {
7992 details->set(2,
7993 caught_exception ? Heap::true_value()
7994 : Heap::false_value());
7995 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
7996 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
7997 }
7998
7999 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008000 }
8001 if (i < length - 1) {
8002 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8003 }
8004 }
8005
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008006 return Heap::undefined_value();
8007}
8008
8009
8010static Object* Runtime_DebugGetProperty(Arguments args) {
8011 HandleScope scope;
8012
8013 ASSERT(args.length() == 2);
8014
8015 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8016 CONVERT_ARG_CHECKED(String, name, 1);
8017
8018 LookupResult result;
8019 obj->Lookup(*name, &result);
8020 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008021 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008022 }
8023 return Heap::undefined_value();
8024}
8025
8026
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008027// Return the property type calculated from the property details.
8028// args[0]: smi with property details.
8029static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8030 ASSERT(args.length() == 1);
8031 CONVERT_CHECKED(Smi, details, args[0]);
8032 PropertyType type = PropertyDetails(details).type();
8033 return Smi::FromInt(static_cast<int>(type));
8034}
8035
8036
8037// Return the property attribute calculated from the property details.
8038// args[0]: smi with property details.
8039static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8040 ASSERT(args.length() == 1);
8041 CONVERT_CHECKED(Smi, details, args[0]);
8042 PropertyAttributes attributes = PropertyDetails(details).attributes();
8043 return Smi::FromInt(static_cast<int>(attributes));
8044}
8045
8046
8047// Return the property insertion index calculated from the property details.
8048// args[0]: smi with property details.
8049static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8050 ASSERT(args.length() == 1);
8051 CONVERT_CHECKED(Smi, details, args[0]);
8052 int index = PropertyDetails(details).index();
8053 return Smi::FromInt(index);
8054}
8055
8056
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008057// Return property value from named interceptor.
8058// args[0]: object
8059// args[1]: property name
8060static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8061 HandleScope scope;
8062 ASSERT(args.length() == 2);
8063 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8064 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8065 CONVERT_ARG_CHECKED(String, name, 1);
8066
8067 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008068 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008069}
8070
8071
8072// Return element value from indexed interceptor.
8073// args[0]: object
8074// args[1]: index
8075static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8076 HandleScope scope;
8077 ASSERT(args.length() == 2);
8078 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8079 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8080 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8081
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008082 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008083}
8084
8085
8086static Object* Runtime_CheckExecutionState(Arguments args) {
8087 ASSERT(args.length() >= 1);
8088 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008089 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008090 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008091 return Top::Throw(Heap::illegal_execution_state_symbol());
8092 }
8093
8094 return Heap::true_value();
8095}
8096
8097
8098static Object* Runtime_GetFrameCount(Arguments args) {
8099 HandleScope scope;
8100 ASSERT(args.length() == 1);
8101
8102 // Check arguments.
8103 Object* result = Runtime_CheckExecutionState(args);
8104 if (result->IsFailure()) return result;
8105
8106 // Count all frames which are relevant to debugging stack trace.
8107 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008108 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008109 if (id == StackFrame::NO_ID) {
8110 // If there is no JavaScript stack frame count is 0.
8111 return Smi::FromInt(0);
8112 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008113 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8114 return Smi::FromInt(n);
8115}
8116
8117
8118static const int kFrameDetailsFrameIdIndex = 0;
8119static const int kFrameDetailsReceiverIndex = 1;
8120static const int kFrameDetailsFunctionIndex = 2;
8121static const int kFrameDetailsArgumentCountIndex = 3;
8122static const int kFrameDetailsLocalCountIndex = 4;
8123static const int kFrameDetailsSourcePositionIndex = 5;
8124static const int kFrameDetailsConstructCallIndex = 6;
8125static const int kFrameDetailsDebuggerFrameIndex = 7;
8126static const int kFrameDetailsFirstDynamicIndex = 8;
8127
8128// Return an array with frame details
8129// args[0]: number: break id
8130// args[1]: number: frame index
8131//
8132// The array returned contains the following information:
8133// 0: Frame id
8134// 1: Receiver
8135// 2: Function
8136// 3: Argument count
8137// 4: Local count
8138// 5: Source position
8139// 6: Constructor call
8140// 7: Debugger frame
8141// Arguments name, value
8142// Locals name, value
8143static Object* Runtime_GetFrameDetails(Arguments args) {
8144 HandleScope scope;
8145 ASSERT(args.length() == 2);
8146
8147 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008148 Object* check = Runtime_CheckExecutionState(args);
8149 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008150 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8151
8152 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008153 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008154 if (id == StackFrame::NO_ID) {
8155 // If there are no JavaScript stack frames return undefined.
8156 return Heap::undefined_value();
8157 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008158 int count = 0;
8159 JavaScriptFrameIterator it(id);
8160 for (; !it.done(); it.Advance()) {
8161 if (count == index) break;
8162 count++;
8163 }
8164 if (it.done()) return Heap::undefined_value();
8165
8166 // Traverse the saved contexts chain to find the active context for the
8167 // selected frame.
8168 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008169 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008170 save = save->prev();
8171 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008172 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008173
8174 // Get the frame id.
8175 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8176
8177 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008178 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008179
8180 // Check for constructor frame.
8181 bool constructor = it.frame()->IsConstructor();
8182
8183 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008184 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008185 ScopeInfo<> info(*code);
8186
8187 // Get the context.
8188 Handle<Context> context(Context::cast(it.frame()->context()));
8189
8190 // Get the locals names and values into a temporary array.
8191 //
8192 // TODO(1240907): Hide compiler-introduced stack variables
8193 // (e.g. .result)? For users of the debugger, they will probably be
8194 // confusing.
8195 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8196 for (int i = 0; i < info.NumberOfLocals(); i++) {
8197 // Name of the local.
8198 locals->set(i * 2, *info.LocalName(i));
8199
8200 // Fetch the value of the local - either from the stack or from a
8201 // heap-allocated context.
8202 if (i < info.number_of_stack_slots()) {
8203 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8204 } else {
8205 Handle<String> name = info.LocalName(i);
8206 // Traverse the context chain to the function context as all local
8207 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008208 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008209 context = Handle<Context>(context->previous());
8210 }
8211 ASSERT(context->is_function_context());
8212 locals->set(i * 2 + 1,
8213 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8214 NULL)));
8215 }
8216 }
8217
8218 // Now advance to the arguments adapter frame (if any). If contains all
8219 // the provided parameters and
8220
8221 // Now advance to the arguments adapter frame (if any). It contains all
8222 // the provided parameters whereas the function frame always have the number
8223 // of arguments matching the functions parameters. The rest of the
8224 // information (except for what is collected above) is the same.
8225 it.AdvanceToArgumentsFrame();
8226
8227 // Find the number of arguments to fill. At least fill the number of
8228 // parameters for the function and fill more if more parameters are provided.
8229 int argument_count = info.number_of_parameters();
8230 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8231 argument_count = it.frame()->GetProvidedParametersCount();
8232 }
8233
8234 // Calculate the size of the result.
8235 int details_size = kFrameDetailsFirstDynamicIndex +
8236 2 * (argument_count + info.NumberOfLocals());
8237 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8238
8239 // Add the frame id.
8240 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8241
8242 // Add the function (same as in function frame).
8243 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8244
8245 // Add the arguments count.
8246 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8247
8248 // Add the locals count
8249 details->set(kFrameDetailsLocalCountIndex,
8250 Smi::FromInt(info.NumberOfLocals()));
8251
8252 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008253 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008254 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8255 } else {
8256 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8257 }
8258
8259 // Add the constructor information.
8260 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8261
8262 // Add information on whether this frame is invoked in the debugger context.
8263 details->set(kFrameDetailsDebuggerFrameIndex,
8264 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8265
8266 // Fill the dynamic part.
8267 int details_index = kFrameDetailsFirstDynamicIndex;
8268
8269 // Add arguments name and value.
8270 for (int i = 0; i < argument_count; i++) {
8271 // Name of the argument.
8272 if (i < info.number_of_parameters()) {
8273 details->set(details_index++, *info.parameter_name(i));
8274 } else {
8275 details->set(details_index++, Heap::undefined_value());
8276 }
8277
8278 // Parameter value.
8279 if (i < it.frame()->GetProvidedParametersCount()) {
8280 details->set(details_index++, it.frame()->GetParameter(i));
8281 } else {
8282 details->set(details_index++, Heap::undefined_value());
8283 }
8284 }
8285
8286 // Add locals name and value from the temporary copy from the function frame.
8287 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8288 details->set(details_index++, locals->get(i));
8289 }
8290
8291 // Add the receiver (same as in function frame).
8292 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8293 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8294 Handle<Object> receiver(it.frame()->receiver());
8295 if (!receiver->IsJSObject()) {
8296 // If the receiver is NOT a JSObject we have hit an optimization
8297 // where a value object is not converted into a wrapped JS objects.
8298 // To hide this optimization from the debugger, we wrap the receiver
8299 // by creating correct wrapper object based on the calling frame's
8300 // global context.
8301 it.Advance();
8302 Handle<Context> calling_frames_global_context(
8303 Context::cast(Context::cast(it.frame()->context())->global_context()));
8304 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8305 }
8306 details->set(kFrameDetailsReceiverIndex, *receiver);
8307
8308 ASSERT_EQ(details_size, details_index);
8309 return *Factory::NewJSArrayWithElements(details);
8310}
8311
8312
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008313// Copy all the context locals into an object used to materialize a scope.
8314static void CopyContextLocalsToScopeObject(Handle<Code> code,
8315 ScopeInfo<>& scope_info,
8316 Handle<Context> context,
8317 Handle<JSObject> scope_object) {
8318 // Fill all context locals to the context extension.
8319 for (int i = Context::MIN_CONTEXT_SLOTS;
8320 i < scope_info.number_of_context_slots();
8321 i++) {
8322 int context_index =
8323 ScopeInfo<>::ContextSlotIndex(*code,
8324 *scope_info.context_slot_name(i),
8325 NULL);
8326
8327 // Don't include the arguments shadow (.arguments) context variable.
8328 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8329 SetProperty(scope_object,
8330 scope_info.context_slot_name(i),
8331 Handle<Object>(context->get(context_index)), NONE);
8332 }
8333 }
8334}
8335
8336
8337// Create a plain JSObject which materializes the local scope for the specified
8338// frame.
8339static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8340 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8341 Handle<Code> code(function->code());
8342 ScopeInfo<> scope_info(*code);
8343
8344 // Allocate and initialize a JSObject with all the arguments, stack locals
8345 // heap locals and extension properties of the debugged function.
8346 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8347
8348 // First fill all parameters.
8349 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8350 SetProperty(local_scope,
8351 scope_info.parameter_name(i),
8352 Handle<Object>(frame->GetParameter(i)), NONE);
8353 }
8354
8355 // Second fill all stack locals.
8356 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8357 SetProperty(local_scope,
8358 scope_info.stack_slot_name(i),
8359 Handle<Object>(frame->GetExpression(i)), NONE);
8360 }
8361
8362 // Third fill all context locals.
8363 Handle<Context> frame_context(Context::cast(frame->context()));
8364 Handle<Context> function_context(frame_context->fcontext());
8365 CopyContextLocalsToScopeObject(code, scope_info,
8366 function_context, local_scope);
8367
8368 // Finally copy any properties from the function context extension. This will
8369 // be variables introduced by eval.
8370 if (function_context->closure() == *function) {
8371 if (function_context->has_extension() &&
8372 !function_context->IsGlobalContext()) {
8373 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008374 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008375 for (int i = 0; i < keys->length(); i++) {
8376 // Names of variables introduced by eval are strings.
8377 ASSERT(keys->get(i)->IsString());
8378 Handle<String> key(String::cast(keys->get(i)));
8379 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8380 }
8381 }
8382 }
8383 return local_scope;
8384}
8385
8386
8387// Create a plain JSObject which materializes the closure content for the
8388// context.
8389static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8390 ASSERT(context->is_function_context());
8391
8392 Handle<Code> code(context->closure()->code());
8393 ScopeInfo<> scope_info(*code);
8394
8395 // Allocate and initialize a JSObject with all the content of theis function
8396 // closure.
8397 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8398
8399 // Check whether the arguments shadow object exists.
8400 int arguments_shadow_index =
8401 ScopeInfo<>::ContextSlotIndex(*code,
8402 Heap::arguments_shadow_symbol(),
8403 NULL);
8404 if (arguments_shadow_index >= 0) {
8405 // In this case all the arguments are available in the arguments shadow
8406 // object.
8407 Handle<JSObject> arguments_shadow(
8408 JSObject::cast(context->get(arguments_shadow_index)));
8409 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8410 SetProperty(closure_scope,
8411 scope_info.parameter_name(i),
8412 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8413 }
8414 }
8415
8416 // Fill all context locals to the context extension.
8417 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8418
8419 // Finally copy any properties from the function context extension. This will
8420 // be variables introduced by eval.
8421 if (context->has_extension()) {
8422 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008423 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008424 for (int i = 0; i < keys->length(); i++) {
8425 // Names of variables introduced by eval are strings.
8426 ASSERT(keys->get(i)->IsString());
8427 Handle<String> key(String::cast(keys->get(i)));
8428 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8429 }
8430 }
8431
8432 return closure_scope;
8433}
8434
8435
8436// Iterate over the actual scopes visible from a stack frame. All scopes are
8437// backed by an actual context except the local scope, which is inserted
8438// "artifically" in the context chain.
8439class ScopeIterator {
8440 public:
8441 enum ScopeType {
8442 ScopeTypeGlobal = 0,
8443 ScopeTypeLocal,
8444 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008445 ScopeTypeClosure,
8446 // Every catch block contains an implicit with block (its parameter is
8447 // a JSContextExtensionObject) that extends current scope with a variable
8448 // holding exception object. Such with blocks are treated as scopes of their
8449 // own type.
8450 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008451 };
8452
8453 explicit ScopeIterator(JavaScriptFrame* frame)
8454 : frame_(frame),
8455 function_(JSFunction::cast(frame->function())),
8456 context_(Context::cast(frame->context())),
8457 local_done_(false),
8458 at_local_(false) {
8459
8460 // Check whether the first scope is actually a local scope.
8461 if (context_->IsGlobalContext()) {
8462 // If there is a stack slot for .result then this local scope has been
8463 // created for evaluating top level code and it is not a real local scope.
8464 // Checking for the existence of .result seems fragile, but the scope info
8465 // saved with the code object does not otherwise have that information.
8466 Handle<Code> code(function_->code());
8467 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8468 at_local_ = index < 0;
8469 } else if (context_->is_function_context()) {
8470 at_local_ = true;
8471 }
8472 }
8473
8474 // More scopes?
8475 bool Done() { return context_.is_null(); }
8476
8477 // Move to the next scope.
8478 void Next() {
8479 // If at a local scope mark the local scope as passed.
8480 if (at_local_) {
8481 at_local_ = false;
8482 local_done_ = true;
8483
8484 // If the current context is not associated with the local scope the
8485 // current context is the next real scope, so don't move to the next
8486 // context in this case.
8487 if (context_->closure() != *function_) {
8488 return;
8489 }
8490 }
8491
8492 // The global scope is always the last in the chain.
8493 if (context_->IsGlobalContext()) {
8494 context_ = Handle<Context>();
8495 return;
8496 }
8497
8498 // Move to the next context.
8499 if (context_->is_function_context()) {
8500 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8501 } else {
8502 context_ = Handle<Context>(context_->previous());
8503 }
8504
8505 // If passing the local scope indicate that the current scope is now the
8506 // local scope.
8507 if (!local_done_ &&
8508 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8509 at_local_ = true;
8510 }
8511 }
8512
8513 // Return the type of the current scope.
8514 int Type() {
8515 if (at_local_) {
8516 return ScopeTypeLocal;
8517 }
8518 if (context_->IsGlobalContext()) {
8519 ASSERT(context_->global()->IsGlobalObject());
8520 return ScopeTypeGlobal;
8521 }
8522 if (context_->is_function_context()) {
8523 return ScopeTypeClosure;
8524 }
8525 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008526 // Current scope is either an explicit with statement or a with statement
8527 // implicitely generated for a catch block.
8528 // If the extension object here is a JSContextExtensionObject then
8529 // current with statement is one frome a catch block otherwise it's a
8530 // regular with statement.
8531 if (context_->extension()->IsJSContextExtensionObject()) {
8532 return ScopeTypeCatch;
8533 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008534 return ScopeTypeWith;
8535 }
8536
8537 // Return the JavaScript object with the content of the current scope.
8538 Handle<JSObject> ScopeObject() {
8539 switch (Type()) {
8540 case ScopeIterator::ScopeTypeGlobal:
8541 return Handle<JSObject>(CurrentContext()->global());
8542 break;
8543 case ScopeIterator::ScopeTypeLocal:
8544 // Materialize the content of the local scope into a JSObject.
8545 return MaterializeLocalScope(frame_);
8546 break;
8547 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008548 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008549 // Return the with object.
8550 return Handle<JSObject>(CurrentContext()->extension());
8551 break;
8552 case ScopeIterator::ScopeTypeClosure:
8553 // Materialize the content of the closure scope into a JSObject.
8554 return MaterializeClosure(CurrentContext());
8555 break;
8556 }
8557 UNREACHABLE();
8558 return Handle<JSObject>();
8559 }
8560
8561 // Return the context for this scope. For the local context there might not
8562 // be an actual context.
8563 Handle<Context> CurrentContext() {
8564 if (at_local_ && context_->closure() != *function_) {
8565 return Handle<Context>();
8566 }
8567 return context_;
8568 }
8569
8570#ifdef DEBUG
8571 // Debug print of the content of the current scope.
8572 void DebugPrint() {
8573 switch (Type()) {
8574 case ScopeIterator::ScopeTypeGlobal:
8575 PrintF("Global:\n");
8576 CurrentContext()->Print();
8577 break;
8578
8579 case ScopeIterator::ScopeTypeLocal: {
8580 PrintF("Local:\n");
8581 Handle<Code> code(function_->code());
8582 ScopeInfo<> scope_info(*code);
8583 scope_info.Print();
8584 if (!CurrentContext().is_null()) {
8585 CurrentContext()->Print();
8586 if (CurrentContext()->has_extension()) {
8587 Handle<JSObject> extension =
8588 Handle<JSObject>(CurrentContext()->extension());
8589 if (extension->IsJSContextExtensionObject()) {
8590 extension->Print();
8591 }
8592 }
8593 }
8594 break;
8595 }
8596
8597 case ScopeIterator::ScopeTypeWith: {
8598 PrintF("With:\n");
8599 Handle<JSObject> extension =
8600 Handle<JSObject>(CurrentContext()->extension());
8601 extension->Print();
8602 break;
8603 }
8604
ager@chromium.orga1645e22009-09-09 19:27:10 +00008605 case ScopeIterator::ScopeTypeCatch: {
8606 PrintF("Catch:\n");
8607 Handle<JSObject> extension =
8608 Handle<JSObject>(CurrentContext()->extension());
8609 extension->Print();
8610 break;
8611 }
8612
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008613 case ScopeIterator::ScopeTypeClosure: {
8614 PrintF("Closure:\n");
8615 CurrentContext()->Print();
8616 if (CurrentContext()->has_extension()) {
8617 Handle<JSObject> extension =
8618 Handle<JSObject>(CurrentContext()->extension());
8619 if (extension->IsJSContextExtensionObject()) {
8620 extension->Print();
8621 }
8622 }
8623 break;
8624 }
8625
8626 default:
8627 UNREACHABLE();
8628 }
8629 PrintF("\n");
8630 }
8631#endif
8632
8633 private:
8634 JavaScriptFrame* frame_;
8635 Handle<JSFunction> function_;
8636 Handle<Context> context_;
8637 bool local_done_;
8638 bool at_local_;
8639
8640 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8641};
8642
8643
8644static Object* Runtime_GetScopeCount(Arguments args) {
8645 HandleScope scope;
8646 ASSERT(args.length() == 2);
8647
8648 // Check arguments.
8649 Object* check = Runtime_CheckExecutionState(args);
8650 if (check->IsFailure()) return check;
8651 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8652
8653 // Get the frame where the debugging is performed.
8654 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8655 JavaScriptFrameIterator it(id);
8656 JavaScriptFrame* frame = it.frame();
8657
8658 // Count the visible scopes.
8659 int n = 0;
8660 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8661 n++;
8662 }
8663
8664 return Smi::FromInt(n);
8665}
8666
8667
8668static const int kScopeDetailsTypeIndex = 0;
8669static const int kScopeDetailsObjectIndex = 1;
8670static const int kScopeDetailsSize = 2;
8671
8672// Return an array with scope details
8673// args[0]: number: break id
8674// args[1]: number: frame index
8675// args[2]: number: scope index
8676//
8677// The array returned contains the following information:
8678// 0: Scope type
8679// 1: Scope object
8680static Object* Runtime_GetScopeDetails(Arguments args) {
8681 HandleScope scope;
8682 ASSERT(args.length() == 3);
8683
8684 // Check arguments.
8685 Object* check = Runtime_CheckExecutionState(args);
8686 if (check->IsFailure()) return check;
8687 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8688 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8689
8690 // Get the frame where the debugging is performed.
8691 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8692 JavaScriptFrameIterator frame_it(id);
8693 JavaScriptFrame* frame = frame_it.frame();
8694
8695 // Find the requested scope.
8696 int n = 0;
8697 ScopeIterator it(frame);
8698 for (; !it.Done() && n < index; it.Next()) {
8699 n++;
8700 }
8701 if (it.Done()) {
8702 return Heap::undefined_value();
8703 }
8704
8705 // Calculate the size of the result.
8706 int details_size = kScopeDetailsSize;
8707 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8708
8709 // Fill in scope details.
8710 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8711 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8712
8713 return *Factory::NewJSArrayWithElements(details);
8714}
8715
8716
8717static Object* Runtime_DebugPrintScopes(Arguments args) {
8718 HandleScope scope;
8719 ASSERT(args.length() == 0);
8720
8721#ifdef DEBUG
8722 // Print the scopes for the top frame.
8723 StackFrameLocator locator;
8724 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8725 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8726 it.DebugPrint();
8727 }
8728#endif
8729 return Heap::undefined_value();
8730}
8731
8732
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008733static Object* Runtime_GetCFrames(Arguments args) {
8734 HandleScope scope;
8735 ASSERT(args.length() == 1);
8736 Object* result = Runtime_CheckExecutionState(args);
8737 if (result->IsFailure()) return result;
8738
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008739#if V8_HOST_ARCH_64_BIT
8740 UNIMPLEMENTED();
8741 return Heap::undefined_value();
8742#else
8743
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008744 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008745 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8746 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008747 if (frames_count == OS::kStackWalkError) {
8748 return Heap::undefined_value();
8749 }
8750
8751 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8752 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8753 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8754 for (int i = 0; i < frames_count; i++) {
8755 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8756 frame_value->SetProperty(
8757 *address_str,
8758 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8759 NONE);
8760
8761 // Get the stack walk text for this frame.
8762 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008763 int frame_text_length = StrLength(frames[i].text);
8764 if (frame_text_length > 0) {
8765 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008766 frame_text = Factory::NewStringFromAscii(str);
8767 }
8768
8769 if (!frame_text.is_null()) {
8770 frame_value->SetProperty(*text_str, *frame_text, NONE);
8771 }
8772
8773 frames_array->set(i, *frame_value);
8774 }
8775 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008776#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008777}
8778
8779
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008780static Object* Runtime_GetThreadCount(Arguments args) {
8781 HandleScope scope;
8782 ASSERT(args.length() == 1);
8783
8784 // Check arguments.
8785 Object* result = Runtime_CheckExecutionState(args);
8786 if (result->IsFailure()) return result;
8787
8788 // Count all archived V8 threads.
8789 int n = 0;
8790 for (ThreadState* thread = ThreadState::FirstInUse();
8791 thread != NULL;
8792 thread = thread->Next()) {
8793 n++;
8794 }
8795
8796 // Total number of threads is current thread and archived threads.
8797 return Smi::FromInt(n + 1);
8798}
8799
8800
8801static const int kThreadDetailsCurrentThreadIndex = 0;
8802static const int kThreadDetailsThreadIdIndex = 1;
8803static const int kThreadDetailsSize = 2;
8804
8805// Return an array with thread details
8806// args[0]: number: break id
8807// args[1]: number: thread index
8808//
8809// The array returned contains the following information:
8810// 0: Is current thread?
8811// 1: Thread id
8812static Object* Runtime_GetThreadDetails(Arguments args) {
8813 HandleScope scope;
8814 ASSERT(args.length() == 2);
8815
8816 // Check arguments.
8817 Object* check = Runtime_CheckExecutionState(args);
8818 if (check->IsFailure()) return check;
8819 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8820
8821 // Allocate array for result.
8822 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8823
8824 // Thread index 0 is current thread.
8825 if (index == 0) {
8826 // Fill the details.
8827 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8828 details->set(kThreadDetailsThreadIdIndex,
8829 Smi::FromInt(ThreadManager::CurrentId()));
8830 } else {
8831 // Find the thread with the requested index.
8832 int n = 1;
8833 ThreadState* thread = ThreadState::FirstInUse();
8834 while (index != n && thread != NULL) {
8835 thread = thread->Next();
8836 n++;
8837 }
8838 if (thread == NULL) {
8839 return Heap::undefined_value();
8840 }
8841
8842 // Fill the details.
8843 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8844 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8845 }
8846
8847 // Convert to JS array and return.
8848 return *Factory::NewJSArrayWithElements(details);
8849}
8850
8851
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008852static Object* Runtime_GetBreakLocations(Arguments args) {
8853 HandleScope scope;
8854 ASSERT(args.length() == 1);
8855
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008856 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8857 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008858 // Find the number of break points
8859 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8860 if (break_locations->IsUndefined()) return Heap::undefined_value();
8861 // Return array as JS array
8862 return *Factory::NewJSArrayWithElements(
8863 Handle<FixedArray>::cast(break_locations));
8864}
8865
8866
8867// Set a break point in a function
8868// args[0]: function
8869// args[1]: number: break source position (within the function source)
8870// args[2]: number: break point object
8871static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8872 HandleScope scope;
8873 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008874 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8875 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008876 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8877 RUNTIME_ASSERT(source_position >= 0);
8878 Handle<Object> break_point_object_arg = args.at<Object>(2);
8879
8880 // Set break point.
8881 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8882
8883 return Heap::undefined_value();
8884}
8885
8886
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008887Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8888 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008889 // Iterate the heap looking for SharedFunctionInfo generated from the
8890 // script. The inner most SharedFunctionInfo containing the source position
8891 // for the requested break point is found.
8892 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8893 // which is found is not compiled it is compiled and the heap is iterated
8894 // again as the compilation might create inner functions from the newly
8895 // compiled function and the actual requested break point might be in one of
8896 // these functions.
8897 bool done = false;
8898 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008899 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008900 Handle<SharedFunctionInfo> target;
8901 // The current candidate for the last function in script:
8902 Handle<SharedFunctionInfo> last;
8903 while (!done) {
8904 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008905 for (HeapObject* obj = iterator.next();
8906 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008907 if (obj->IsSharedFunctionInfo()) {
8908 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
8909 if (shared->script() == *script) {
8910 // If the SharedFunctionInfo found has the requested script data and
8911 // contains the source position it is a candidate.
8912 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00008913 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008914 start_position = shared->start_position();
8915 }
8916 if (start_position <= position &&
8917 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00008918 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008919 // candidate this is the new candidate.
8920 if (target.is_null()) {
8921 target_start_position = start_position;
8922 target = shared;
8923 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00008924 if (target_start_position == start_position &&
8925 shared->end_position() == target->end_position()) {
8926 // If a top-level function contain only one function
8927 // declartion the source for the top-level and the function is
8928 // the same. In that case prefer the non top-level function.
8929 if (!shared->is_toplevel()) {
8930 target_start_position = start_position;
8931 target = shared;
8932 }
8933 } else if (target_start_position <= start_position &&
8934 shared->end_position() <= target->end_position()) {
8935 // This containment check includes equality as a function inside
8936 // a top-level function can share either start or end position
8937 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008938 target_start_position = start_position;
8939 target = shared;
8940 }
8941 }
8942 }
8943
8944 // Keep track of the last function in the script.
8945 if (last.is_null() ||
8946 shared->end_position() > last->start_position()) {
8947 last = shared;
8948 }
8949 }
8950 }
8951 }
8952
8953 // Make sure some candidate is selected.
8954 if (target.is_null()) {
8955 if (!last.is_null()) {
8956 // Position after the last function - use last.
8957 target = last;
8958 } else {
8959 // Unable to find function - possibly script without any function.
8960 return Heap::undefined_value();
8961 }
8962 }
8963
8964 // If the candidate found is compiled we are done. NOTE: when lazy
8965 // compilation of inner functions is introduced some additional checking
8966 // needs to be done here to compile inner functions.
8967 done = target->is_compiled();
8968 if (!done) {
8969 // If the candidate is not compiled compile it to reveal any inner
8970 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008971 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008972 }
8973 }
8974
8975 return *target;
8976}
8977
8978
8979// Change the state of a break point in a script. NOTE: Regarding performance
8980// see the NOTE for GetScriptFromScriptData.
8981// args[0]: script to set break point in
8982// args[1]: number: break source position (within the script source)
8983// args[2]: number: break point object
8984static Object* Runtime_SetScriptBreakPoint(Arguments args) {
8985 HandleScope scope;
8986 ASSERT(args.length() == 3);
8987 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
8988 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8989 RUNTIME_ASSERT(source_position >= 0);
8990 Handle<Object> break_point_object_arg = args.at<Object>(2);
8991
8992 // Get the script from the script wrapper.
8993 RUNTIME_ASSERT(wrapper->value()->IsScript());
8994 Handle<Script> script(Script::cast(wrapper->value()));
8995
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008996 Object* result = Runtime::FindSharedFunctionInfoInScript(
8997 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008998 if (!result->IsUndefined()) {
8999 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9000 // Find position within function. The script position might be before the
9001 // source position of the first function.
9002 int position;
9003 if (shared->start_position() > source_position) {
9004 position = 0;
9005 } else {
9006 position = source_position - shared->start_position();
9007 }
9008 Debug::SetBreakPoint(shared, position, break_point_object_arg);
9009 }
9010 return Heap::undefined_value();
9011}
9012
9013
9014// Clear a break point
9015// args[0]: number: break point object
9016static Object* Runtime_ClearBreakPoint(Arguments args) {
9017 HandleScope scope;
9018 ASSERT(args.length() == 1);
9019 Handle<Object> break_point_object_arg = args.at<Object>(0);
9020
9021 // Clear break point.
9022 Debug::ClearBreakPoint(break_point_object_arg);
9023
9024 return Heap::undefined_value();
9025}
9026
9027
9028// Change the state of break on exceptions
9029// args[0]: boolean indicating uncaught exceptions
9030// args[1]: boolean indicating on/off
9031static Object* Runtime_ChangeBreakOnException(Arguments args) {
9032 HandleScope scope;
9033 ASSERT(args.length() == 2);
9034 ASSERT(args[0]->IsNumber());
9035 ASSERT(args[1]->IsBoolean());
9036
9037 // Update break point state
9038 ExceptionBreakType type =
9039 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9040 bool enable = args[1]->ToBoolean()->IsTrue();
9041 Debug::ChangeBreakOnException(type, enable);
9042 return Heap::undefined_value();
9043}
9044
9045
9046// Prepare for stepping
9047// args[0]: break id for checking execution state
9048// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009049// args[2]: number of times to perform the step, for step out it is the number
9050// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009051static Object* Runtime_PrepareStep(Arguments args) {
9052 HandleScope scope;
9053 ASSERT(args.length() == 3);
9054 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009055 Object* check = Runtime_CheckExecutionState(args);
9056 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009057 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9058 return Top::Throw(Heap::illegal_argument_symbol());
9059 }
9060
9061 // Get the step action and check validity.
9062 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9063 if (step_action != StepIn &&
9064 step_action != StepNext &&
9065 step_action != StepOut &&
9066 step_action != StepInMin &&
9067 step_action != StepMin) {
9068 return Top::Throw(Heap::illegal_argument_symbol());
9069 }
9070
9071 // Get the number of steps.
9072 int step_count = NumberToInt32(args[2]);
9073 if (step_count < 1) {
9074 return Top::Throw(Heap::illegal_argument_symbol());
9075 }
9076
ager@chromium.orga1645e22009-09-09 19:27:10 +00009077 // Clear all current stepping setup.
9078 Debug::ClearStepping();
9079
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009080 // Prepare step.
9081 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9082 return Heap::undefined_value();
9083}
9084
9085
9086// Clear all stepping set by PrepareStep.
9087static Object* Runtime_ClearStepping(Arguments args) {
9088 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009089 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009090 Debug::ClearStepping();
9091 return Heap::undefined_value();
9092}
9093
9094
9095// Creates a copy of the with context chain. The copy of the context chain is
9096// is linked to the function context supplied.
9097static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9098 Handle<Context> function_context) {
9099 // At the bottom of the chain. Return the function context to link to.
9100 if (context_chain->is_function_context()) {
9101 return function_context;
9102 }
9103
9104 // Recursively copy the with contexts.
9105 Handle<Context> previous(context_chain->previous());
9106 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9107 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009108 CopyWithContextChain(function_context, previous),
9109 extension,
9110 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009111}
9112
9113
9114// Helper function to find or create the arguments object for
9115// Runtime_DebugEvaluate.
9116static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9117 Handle<JSFunction> function,
9118 Handle<Code> code,
9119 const ScopeInfo<>* sinfo,
9120 Handle<Context> function_context) {
9121 // Try to find the value of 'arguments' to pass as parameter. If it is not
9122 // found (that is the debugged function does not reference 'arguments' and
9123 // does not support eval) then create an 'arguments' object.
9124 int index;
9125 if (sinfo->number_of_stack_slots() > 0) {
9126 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9127 if (index != -1) {
9128 return Handle<Object>(frame->GetExpression(index));
9129 }
9130 }
9131
9132 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9133 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9134 NULL);
9135 if (index != -1) {
9136 return Handle<Object>(function_context->get(index));
9137 }
9138 }
9139
9140 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009141 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9142 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009143
9144 AssertNoAllocation no_gc;
9145 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009146 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009147 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009148 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009149 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009150 return arguments;
9151}
9152
9153
9154// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009155// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009156// extension part has all the parameters and locals of the function on the
9157// stack frame. A function which calls eval with the code to evaluate is then
9158// compiled in this context and called in this context. As this context
9159// replaces the context of the function on the stack frame a new (empty)
9160// function is created as well to be used as the closure for the context.
9161// This function and the context acts as replacements for the function on the
9162// stack frame presenting the same view of the values of parameters and
9163// local variables as if the piece of JavaScript was evaluated at the point
9164// where the function on the stack frame is currently stopped.
9165static Object* Runtime_DebugEvaluate(Arguments args) {
9166 HandleScope scope;
9167
9168 // Check the execution state and decode arguments frame and source to be
9169 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009170 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009171 Object* check_result = Runtime_CheckExecutionState(args);
9172 if (check_result->IsFailure()) return check_result;
9173 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9174 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009175 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9176
9177 // Handle the processing of break.
9178 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009179
9180 // Get the frame where the debugging is performed.
9181 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9182 JavaScriptFrameIterator it(id);
9183 JavaScriptFrame* frame = it.frame();
9184 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9185 Handle<Code> code(function->code());
9186 ScopeInfo<> sinfo(*code);
9187
9188 // Traverse the saved contexts chain to find the active context for the
9189 // selected frame.
9190 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009191 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009192 save = save->prev();
9193 }
9194 ASSERT(save != NULL);
9195 SaveContext savex;
9196 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009197
9198 // Create the (empty) function replacing the function on the stack frame for
9199 // the purpose of evaluating in the context created below. It is important
9200 // that this function does not describe any parameters and local variables
9201 // in the context. If it does then this will cause problems with the lookup
9202 // in Context::Lookup, where context slots for parameters and local variables
9203 // are looked at before the extension object.
9204 Handle<JSFunction> go_between =
9205 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9206 go_between->set_context(function->context());
9207#ifdef DEBUG
9208 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9209 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9210 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9211#endif
9212
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009213 // Materialize the content of the local scope into a JSObject.
9214 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009215
9216 // Allocate a new context for the debug evaluation and set the extension
9217 // object build.
9218 Handle<Context> context =
9219 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009220 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009221 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009222 Handle<Context> frame_context(Context::cast(frame->context()));
9223 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009224 context = CopyWithContextChain(frame_context, context);
9225
9226 // Wrap the evaluation statement in a new function compiled in the newly
9227 // created context. The function has one parameter which has to be called
9228 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009229 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009230 // function(arguments,__source__) {return eval(__source__);}
9231 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009232 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009233 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009234 Handle<String> function_source =
9235 Factory::NewStringFromAscii(Vector<const char>(source_str,
9236 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009237 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009238 Compiler::CompileEval(function_source,
9239 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009240 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009241 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009242 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009243 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009244 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009245
9246 // Invoke the result of the compilation to get the evaluation function.
9247 bool has_pending_exception;
9248 Handle<Object> receiver(frame->receiver());
9249 Handle<Object> evaluation_function =
9250 Execution::Call(compiled_function, receiver, 0, NULL,
9251 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009252 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009253
9254 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9255 function_context);
9256
9257 // Invoke the evaluation function and return the result.
9258 const int argc = 2;
9259 Object** argv[argc] = { arguments.location(),
9260 Handle<Object>::cast(source).location() };
9261 Handle<Object> result =
9262 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9263 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009264 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009265
9266 // Skip the global proxy as it has no properties and always delegates to the
9267 // real global object.
9268 if (result->IsJSGlobalProxy()) {
9269 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9270 }
9271
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009272 return *result;
9273}
9274
9275
9276static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9277 HandleScope scope;
9278
9279 // Check the execution state and decode arguments frame and source to be
9280 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009281 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009282 Object* check_result = Runtime_CheckExecutionState(args);
9283 if (check_result->IsFailure()) return check_result;
9284 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009285 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9286
9287 // Handle the processing of break.
9288 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009289
9290 // Enter the top context from before the debugger was invoked.
9291 SaveContext save;
9292 SaveContext* top = &save;
9293 while (top != NULL && *top->context() == *Debug::debug_context()) {
9294 top = top->prev();
9295 }
9296 if (top != NULL) {
9297 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009298 }
9299
9300 // Get the global context now set to the top context from before the
9301 // debugger was invoked.
9302 Handle<Context> context = Top::global_context();
9303
9304 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009305 Handle<SharedFunctionInfo> shared =
9306 Compiler::CompileEval(source,
9307 context,
9308 true,
9309 Compiler::DONT_VALIDATE_JSON);
9310 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009311 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009312 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9313 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009314
9315 // Invoke the result of the compilation to get the evaluation function.
9316 bool has_pending_exception;
9317 Handle<Object> receiver = Top::global();
9318 Handle<Object> result =
9319 Execution::Call(compiled_function, receiver, 0, NULL,
9320 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009321 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009322 return *result;
9323}
9324
9325
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009326static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9327 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009328 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009329
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009330 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009331 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009332
9333 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009334 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009335 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9336 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9337 // because using
9338 // instances->set(i, *GetScriptWrapper(script))
9339 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9340 // already have deferenced the instances handle.
9341 Handle<JSValue> wrapper = GetScriptWrapper(script);
9342 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009343 }
9344
9345 // Return result as a JS array.
9346 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9347 Handle<JSArray>::cast(result)->SetContent(*instances);
9348 return *result;
9349}
9350
9351
9352// Helper function used by Runtime_DebugReferencedBy below.
9353static int DebugReferencedBy(JSObject* target,
9354 Object* instance_filter, int max_references,
9355 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009356 JSFunction* arguments_function) {
9357 NoHandleAllocation ha;
9358 AssertNoAllocation no_alloc;
9359
9360 // Iterate the heap.
9361 int count = 0;
9362 JSObject* last = NULL;
9363 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009364 HeapObject* heap_obj = NULL;
9365 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009366 (max_references == 0 || count < max_references)) {
9367 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009368 if (heap_obj->IsJSObject()) {
9369 // Skip context extension objects and argument arrays as these are
9370 // checked in the context of functions using them.
9371 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009372 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009373 obj->map()->constructor() == arguments_function) {
9374 continue;
9375 }
9376
9377 // Check if the JS object has a reference to the object looked for.
9378 if (obj->ReferencesObject(target)) {
9379 // Check instance filter if supplied. This is normally used to avoid
9380 // references from mirror objects (see Runtime_IsInPrototypeChain).
9381 if (!instance_filter->IsUndefined()) {
9382 Object* V = obj;
9383 while (true) {
9384 Object* prototype = V->GetPrototype();
9385 if (prototype->IsNull()) {
9386 break;
9387 }
9388 if (instance_filter == prototype) {
9389 obj = NULL; // Don't add this object.
9390 break;
9391 }
9392 V = prototype;
9393 }
9394 }
9395
9396 if (obj != NULL) {
9397 // Valid reference found add to instance array if supplied an update
9398 // count.
9399 if (instances != NULL && count < instances_size) {
9400 instances->set(count, obj);
9401 }
9402 last = obj;
9403 count++;
9404 }
9405 }
9406 }
9407 }
9408
9409 // Check for circular reference only. This can happen when the object is only
9410 // referenced from mirrors and has a circular reference in which case the
9411 // object is not really alive and would have been garbage collected if not
9412 // referenced from the mirror.
9413 if (count == 1 && last == target) {
9414 count = 0;
9415 }
9416
9417 // Return the number of referencing objects found.
9418 return count;
9419}
9420
9421
9422// Scan the heap for objects with direct references to an object
9423// args[0]: the object to find references to
9424// args[1]: constructor function for instances to exclude (Mirror)
9425// args[2]: the the maximum number of objects to return
9426static Object* Runtime_DebugReferencedBy(Arguments args) {
9427 ASSERT(args.length() == 3);
9428
9429 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009430 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009431
9432 // Check parameters.
9433 CONVERT_CHECKED(JSObject, target, args[0]);
9434 Object* instance_filter = args[1];
9435 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9436 instance_filter->IsJSObject());
9437 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9438 RUNTIME_ASSERT(max_references >= 0);
9439
9440 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009441 JSObject* arguments_boilerplate =
9442 Top::context()->global_context()->arguments_boilerplate();
9443 JSFunction* arguments_function =
9444 JSFunction::cast(arguments_boilerplate->map()->constructor());
9445
9446 // Get the number of referencing objects.
9447 int count;
9448 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009449 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009450
9451 // Allocate an array to hold the result.
9452 Object* object = Heap::AllocateFixedArray(count);
9453 if (object->IsFailure()) return object;
9454 FixedArray* instances = FixedArray::cast(object);
9455
9456 // Fill the referencing objects.
9457 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009458 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009459
9460 // Return result as JS array.
9461 Object* result =
9462 Heap::AllocateJSObject(
9463 Top::context()->global_context()->array_function());
9464 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9465 return result;
9466}
9467
9468
9469// Helper function used by Runtime_DebugConstructedBy below.
9470static int DebugConstructedBy(JSFunction* constructor, int max_references,
9471 FixedArray* instances, int instances_size) {
9472 AssertNoAllocation no_alloc;
9473
9474 // Iterate the heap.
9475 int count = 0;
9476 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009477 HeapObject* heap_obj = NULL;
9478 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009479 (max_references == 0 || count < max_references)) {
9480 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009481 if (heap_obj->IsJSObject()) {
9482 JSObject* obj = JSObject::cast(heap_obj);
9483 if (obj->map()->constructor() == constructor) {
9484 // Valid reference found add to instance array if supplied an update
9485 // count.
9486 if (instances != NULL && count < instances_size) {
9487 instances->set(count, obj);
9488 }
9489 count++;
9490 }
9491 }
9492 }
9493
9494 // Return the number of referencing objects found.
9495 return count;
9496}
9497
9498
9499// Scan the heap for objects constructed by a specific function.
9500// args[0]: the constructor to find instances of
9501// args[1]: the the maximum number of objects to return
9502static Object* Runtime_DebugConstructedBy(Arguments args) {
9503 ASSERT(args.length() == 2);
9504
9505 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009506 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009507
9508 // Check parameters.
9509 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9510 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9511 RUNTIME_ASSERT(max_references >= 0);
9512
9513 // Get the number of referencing objects.
9514 int count;
9515 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9516
9517 // Allocate an array to hold the result.
9518 Object* object = Heap::AllocateFixedArray(count);
9519 if (object->IsFailure()) return object;
9520 FixedArray* instances = FixedArray::cast(object);
9521
9522 // Fill the referencing objects.
9523 count = DebugConstructedBy(constructor, max_references, instances, count);
9524
9525 // Return result as JS array.
9526 Object* result =
9527 Heap::AllocateJSObject(
9528 Top::context()->global_context()->array_function());
9529 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9530 return result;
9531}
9532
9533
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009534// Find the effective prototype object as returned by __proto__.
9535// args[0]: the object to find the prototype for.
9536static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009537 ASSERT(args.length() == 1);
9538
9539 CONVERT_CHECKED(JSObject, obj, args[0]);
9540
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009541 // Use the __proto__ accessor.
9542 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009543}
9544
9545
9546static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009547 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009548 CPU::DebugBreak();
9549 return Heap::undefined_value();
9550}
9551
9552
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009553static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009554#ifdef DEBUG
9555 HandleScope scope;
9556 ASSERT(args.length() == 1);
9557 // Get the function and make sure it is compiled.
9558 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009559 Handle<SharedFunctionInfo> shared(func->shared());
9560 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009561 return Failure::Exception();
9562 }
9563 func->code()->PrintLn();
9564#endif // DEBUG
9565 return Heap::undefined_value();
9566}
ager@chromium.org9085a012009-05-11 19:22:57 +00009567
9568
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009569static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9570#ifdef DEBUG
9571 HandleScope scope;
9572 ASSERT(args.length() == 1);
9573 // Get the function and make sure it is compiled.
9574 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009575 Handle<SharedFunctionInfo> shared(func->shared());
9576 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009577 return Failure::Exception();
9578 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009579 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009580#endif // DEBUG
9581 return Heap::undefined_value();
9582}
9583
9584
ager@chromium.org9085a012009-05-11 19:22:57 +00009585static Object* Runtime_FunctionGetInferredName(Arguments args) {
9586 NoHandleAllocation ha;
9587 ASSERT(args.length() == 1);
9588
9589 CONVERT_CHECKED(JSFunction, f, args[0]);
9590 return f->shared()->inferred_name();
9591}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009592
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009593
9594static int FindSharedFunctionInfosForScript(Script* script,
9595 FixedArray* buffer) {
9596 AssertNoAllocation no_allocations;
9597
9598 int counter = 0;
9599 int buffer_size = buffer->length();
9600 HeapIterator iterator;
9601 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9602 ASSERT(obj != NULL);
9603 if (!obj->IsSharedFunctionInfo()) {
9604 continue;
9605 }
9606 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9607 if (shared->script() != script) {
9608 continue;
9609 }
9610 if (counter < buffer_size) {
9611 buffer->set(counter, shared);
9612 }
9613 counter++;
9614 }
9615 return counter;
9616}
9617
9618// For a script finds all SharedFunctionInfo's in the heap that points
9619// to this script. Returns JSArray of SharedFunctionInfo wrapped
9620// in OpaqueReferences.
9621static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9622 Arguments args) {
9623 ASSERT(args.length() == 1);
9624 HandleScope scope;
9625 CONVERT_CHECKED(JSValue, script_value, args[0]);
9626
9627 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9628
9629 const int kBufferSize = 32;
9630
9631 Handle<FixedArray> array;
9632 array = Factory::NewFixedArray(kBufferSize);
9633 int number = FindSharedFunctionInfosForScript(*script, *array);
9634 if (number > kBufferSize) {
9635 array = Factory::NewFixedArray(number);
9636 FindSharedFunctionInfosForScript(*script, *array);
9637 }
9638
9639 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9640 result->set_length(Smi::FromInt(number));
9641
9642 LiveEdit::WrapSharedFunctionInfos(result);
9643
9644 return *result;
9645}
9646
9647// For a script calculates compilation information about all its functions.
9648// The script source is explicitly specified by the second argument.
9649// The source of the actual script is not used, however it is important that
9650// all generated code keeps references to this particular instance of script.
9651// Returns a JSArray of compilation infos. The array is ordered so that
9652// each function with all its descendant is always stored in a continues range
9653// with the function itself going first. The root function is a script function.
9654static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9655 ASSERT(args.length() == 2);
9656 HandleScope scope;
9657 CONVERT_CHECKED(JSValue, script, args[0]);
9658 CONVERT_ARG_CHECKED(String, source, 1);
9659 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9660
9661 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9662
9663 if (Top::has_pending_exception()) {
9664 return Failure::Exception();
9665 }
9666
9667 return result;
9668}
9669
9670// Changes the source of the script to a new_source and creates a new
9671// script representing the old version of the script source.
9672static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9673 ASSERT(args.length() == 3);
9674 HandleScope scope;
9675 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9676 CONVERT_ARG_CHECKED(String, new_source, 1);
9677 CONVERT_ARG_CHECKED(String, old_script_name, 2);
9678 Handle<Script> original_script =
9679 Handle<Script>(Script::cast(original_script_value->value()));
9680
9681 Handle<String> original_source(String::cast(original_script->source()));
9682
9683 original_script->set_source(*new_source);
9684 Handle<Script> old_script = Factory::NewScript(original_source);
9685 old_script->set_name(*old_script_name);
9686 old_script->set_line_offset(original_script->line_offset());
9687 old_script->set_column_offset(original_script->column_offset());
9688 old_script->set_data(original_script->data());
9689 old_script->set_type(original_script->type());
9690 old_script->set_context_data(original_script->context_data());
9691 old_script->set_compilation_type(original_script->compilation_type());
9692 old_script->set_eval_from_shared(original_script->eval_from_shared());
9693 old_script->set_eval_from_instructions_offset(
9694 original_script->eval_from_instructions_offset());
9695
ager@chromium.org357bf652010-04-12 11:30:10 +00009696 // Drop line ends so that they will be recalculated.
9697 original_script->set_line_ends(Heap::undefined_value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009698
9699 Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
9700
9701 return *(GetScriptWrapper(old_script));
9702}
9703
9704// Replaces code of SharedFunctionInfo with a new one.
9705static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9706 ASSERT(args.length() == 2);
9707 HandleScope scope;
9708 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9709 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9710
9711 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
9712
9713 return Heap::undefined_value();
9714}
9715
9716// Connects SharedFunctionInfo to another script.
9717static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
9718 ASSERT(args.length() == 2);
9719 HandleScope scope;
9720 CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
9721 CONVERT_ARG_CHECKED(JSValue, script_value, 1);
9722 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9723
9724 LiveEdit::RelinkFunctionToScript(shared_info_array, script);
9725
9726 return Heap::undefined_value();
9727}
9728
9729// Updates positions of a shared function info (first parameter) according
9730// to script source change. Text change is described in second parameter as
9731// array of groups of 3 numbers:
9732// (change_begin, change_end, change_end_new_position).
9733// Each group describes a change in text; groups are sorted by change_begin.
ager@chromium.org357bf652010-04-12 11:30:10 +00009734// Returns an array of pairs (new source position, breakpoint_object/array)
9735// so that JS side could update positions in breakpoint objects.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009736static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9737 ASSERT(args.length() == 2);
9738 HandleScope scope;
9739 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9740 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9741
ager@chromium.org357bf652010-04-12 11:30:10 +00009742 Handle<Object> result =
9743 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009744
ager@chromium.org357bf652010-04-12 11:30:10 +00009745 return *result;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009746}
9747
9748
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009749// For array of SharedFunctionInfo's (each wrapped in JSValue)
9750// checks that none of them have activations on stacks (of any thread).
9751// Returns array of the same length with corresponding results of
9752// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009753static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9754 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009755 HandleScope scope;
9756 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009757 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009758
9759
ager@chromium.org357bf652010-04-12 11:30:10 +00009760 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009761}
9762
9763
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009764// A testing entry. Returns statement position which is the closest to
9765// source_position.
9766static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9767 ASSERT(args.length() == 2);
9768 HandleScope scope;
9769 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9770 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9771
9772 Handle<Code> code(function->code());
9773
9774 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9775 int closest_pc = 0;
9776 int distance = kMaxInt;
9777 while (!it.done()) {
9778 int statement_position = static_cast<int>(it.rinfo()->data());
9779 // Check if this break point is closer that what was previously found.
9780 if (source_position <= statement_position &&
9781 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009782 closest_pc =
9783 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009784 distance = statement_position - source_position;
9785 // Check whether we can't get any closer.
9786 if (distance == 0) break;
9787 }
9788 it.next();
9789 }
9790
9791 return Smi::FromInt(closest_pc);
9792}
9793
9794
ager@chromium.org357bf652010-04-12 11:30:10 +00009795// Calls specified function with or without entering the debugger.
9796// This is used in unit tests to run code as if debugger is entered or simply
9797// to have a stack with C++ frame in the middle.
9798static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9799 ASSERT(args.length() == 2);
9800 HandleScope scope;
9801 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9802 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9803
9804 Handle<Object> result;
9805 bool pending_exception;
9806 {
9807 if (without_debugger) {
9808 result = Execution::Call(function, Top::global(), 0, NULL,
9809 &pending_exception);
9810 } else {
9811 EnterDebugger enter_debugger;
9812 result = Execution::Call(function, Top::global(), 0, NULL,
9813 &pending_exception);
9814 }
9815 }
9816 if (!pending_exception) {
9817 return *result;
9818 } else {
9819 return Failure::Exception();
9820 }
9821}
9822
9823
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009824#endif // ENABLE_DEBUGGER_SUPPORT
9825
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009826#ifdef ENABLE_LOGGING_AND_PROFILING
9827
9828static Object* Runtime_ProfilerResume(Arguments args) {
9829 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009830 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009831
9832 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009833 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9834 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009835 return Heap::undefined_value();
9836}
9837
9838
9839static Object* Runtime_ProfilerPause(Arguments args) {
9840 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009841 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009842
9843 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009844 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9845 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009846 return Heap::undefined_value();
9847}
9848
9849#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009850
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009851// Finds the script object from the script data. NOTE: This operation uses
9852// heap traversal to find the function generated for the source position
9853// for the requested break point. For lazily compiled functions several heap
9854// traversals might be required rendering this operation as a rather slow
9855// operation. However for setting break points which is normally done through
9856// some kind of user interaction the performance is not crucial.
9857static Handle<Object> Runtime_GetScriptFromScriptName(
9858 Handle<String> script_name) {
9859 // Scan the heap for Script objects to find the script with the requested
9860 // script data.
9861 Handle<Script> script;
9862 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009863 HeapObject* obj = NULL;
9864 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009865 // If a script is found check if it has the script data requested.
9866 if (obj->IsScript()) {
9867 if (Script::cast(obj)->name()->IsString()) {
9868 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9869 script = Handle<Script>(Script::cast(obj));
9870 }
9871 }
9872 }
9873 }
9874
9875 // If no script with the requested script data is found return undefined.
9876 if (script.is_null()) return Factory::undefined_value();
9877
9878 // Return the script found.
9879 return GetScriptWrapper(script);
9880}
9881
9882
9883// Get the script object from script data. NOTE: Regarding performance
9884// see the NOTE for GetScriptFromScriptData.
9885// args[0]: script data for the script to find the source for
9886static Object* Runtime_GetScript(Arguments args) {
9887 HandleScope scope;
9888
9889 ASSERT(args.length() == 1);
9890
9891 CONVERT_CHECKED(String, script_name, args[0]);
9892
9893 // Find the requested script.
9894 Handle<Object> result =
9895 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
9896 return *result;
9897}
9898
9899
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009900// Determines whether the given stack frame should be displayed in
9901// a stack trace. The caller is the error constructor that asked
9902// for the stack trace to be collected. The first time a construct
9903// call to this function is encountered it is skipped. The seen_caller
9904// in/out parameter is used to remember if the caller has been seen
9905// yet.
9906static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
9907 bool* seen_caller) {
9908 // Only display JS frames.
9909 if (!raw_frame->is_java_script())
9910 return false;
9911 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
9912 Object* raw_fun = frame->function();
9913 // Not sure when this can happen but skip it just in case.
9914 if (!raw_fun->IsJSFunction())
9915 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009916 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009917 *seen_caller = true;
9918 return false;
9919 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009920 // Skip all frames until we've seen the caller. Also, skip the most
9921 // obvious builtin calls. Some builtin calls (such as Number.ADD
9922 // which is invoked using 'call') are very difficult to recognize
9923 // so we're leaving them in for now.
9924 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009925}
9926
9927
9928// Collect the raw data for a stack trace. Returns an array of three
9929// element segments each containing a receiver, function and native
9930// code offset.
9931static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009932 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009933 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009934 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
9935
9936 HandleScope scope;
9937
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009938 limit = Max(limit, 0); // Ensure that limit is not negative.
9939 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009940 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009941
9942 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009943 // If the caller parameter is a function we skip frames until we're
9944 // under it before starting to collect.
9945 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009946 int cursor = 0;
9947 int frames_seen = 0;
9948 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009949 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009950 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009951 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009952 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009953 Object* recv = frame->receiver();
9954 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009955 Address pc = frame->pc();
9956 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009957 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009958 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009959 if (cursor + 2 < elements->length()) {
9960 elements->set(cursor++, recv);
9961 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009962 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009963 } else {
9964 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009965 Handle<Object> recv_handle(recv);
9966 Handle<Object> fun_handle(fun);
9967 SetElement(result, cursor++, recv_handle);
9968 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009969 SetElement(result, cursor++, Handle<Smi>(offset));
9970 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009971 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009972 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009973 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009974
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009975 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009976 return *result;
9977}
9978
9979
ager@chromium.org3811b432009-10-28 14:53:37 +00009980// Returns V8 version as a string.
9981static Object* Runtime_GetV8Version(Arguments args) {
9982 ASSERT_EQ(args.length(), 0);
9983
9984 NoHandleAllocation ha;
9985
9986 const char* version_string = v8::V8::GetVersion();
9987
9988 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
9989}
9990
9991
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009992static Object* Runtime_Abort(Arguments args) {
9993 ASSERT(args.length() == 2);
9994 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
9995 Smi::cast(args[1])->value());
9996 Top::PrintStack();
9997 OS::Abort();
9998 UNREACHABLE();
9999 return NULL;
10000}
10001
10002
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010003static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10004 ASSERT(args.length() == 0);
10005 HandleScope::DeleteExtensions();
10006 return Heap::undefined_value();
10007}
10008
10009
kasper.lund44510672008-07-25 07:37:58 +000010010#ifdef DEBUG
10011// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10012// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010013static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010014 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010015 HandleScope scope;
10016 Handle<JSArray> result = Factory::NewJSArray(0);
10017 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010018 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010019#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010020 { \
10021 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010022 Handle<String> name; \
10023 /* Inline runtime functions have an underscore in front of the name. */ \
10024 if (inline_runtime_functions) { \
10025 name = Factory::NewStringFromAscii( \
10026 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10027 } else { \
10028 name = Factory::NewStringFromAscii( \
10029 Vector<const char>(#Name, StrLength(#Name))); \
10030 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010031 Handle<JSArray> pair = Factory::NewJSArray(0); \
10032 SetElement(pair, 0, name); \
10033 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10034 SetElement(result, index++, pair); \
10035 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010036 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010037 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010038 inline_runtime_functions = true;
10039 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010040#undef ADD_ENTRY
10041 return *result;
10042}
kasper.lund44510672008-07-25 07:37:58 +000010043#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010044
10045
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010046static Object* Runtime_Log(Arguments args) {
10047 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010048 CONVERT_CHECKED(String, format, args[0]);
10049 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010050 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010051 Logger::LogRuntime(chars, elms);
10052 return Heap::undefined_value();
10053}
10054
10055
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010056static Object* Runtime_IS_VAR(Arguments args) {
10057 UNREACHABLE(); // implemented as macro in the parser
10058 return NULL;
10059}
10060
10061
10062// ----------------------------------------------------------------------------
10063// Implementation of Runtime
10064
ager@chromium.orga1645e22009-09-09 19:27:10 +000010065#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010066 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010067 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010068
10069static Runtime::Function Runtime_functions[] = {
10070 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010071 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010072};
10073
10074#undef F
10075
10076
10077Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10078 ASSERT(0 <= fid && fid < kNofFunctions);
10079 return &Runtime_functions[fid];
10080}
10081
10082
10083Runtime::Function* Runtime::FunctionForName(const char* name) {
10084 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10085 if (strcmp(f->name, name) == 0) {
10086 return f;
10087 }
10088 }
10089 return NULL;
10090}
10091
10092
10093void Runtime::PerformGC(Object* result) {
10094 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010095 if (failure->IsRetryAfterGC()) {
10096 // Try to do a garbage collection; ignore it if it fails. The C
10097 // entry stub will throw an out-of-memory exception in that case.
10098 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10099 } else {
10100 // Handle last resort GC and make sure to allow future allocations
10101 // to grow the heap without causing GCs (if possible).
10102 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010103 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010104 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010105}
10106
10107
10108} } // namespace v8::internal