blob: 4e4856dd80ad71cb94532a74815a4b6946b7760d [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.
791 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
792 Handle<JSFunction> function =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +0000793 Factory::NewFunctionFromBoilerplate(boilerplate, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000794 value = function;
795 }
796
797 LookupResult lookup;
798 global->LocalLookup(*name, &lookup);
799
800 PropertyAttributes attributes = is_const_property
801 ? static_cast<PropertyAttributes>(base | READ_ONLY)
802 : base;
803
804 if (lookup.IsProperty()) {
805 // There's a local property that we need to overwrite because
806 // we're either declaring a function or there's an interceptor
807 // that claims the property is absent.
808
809 // Check for conflicting re-declarations. We cannot have
810 // conflicting types in case of intercepted properties because
811 // they are absent.
812 if (lookup.type() != INTERCEPTOR &&
813 (lookup.IsReadOnly() || is_const_property)) {
814 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
815 return ThrowRedeclarationError(type, name);
816 }
817 SetProperty(global, name, value, attributes);
818 } else {
819 // If a property with this name does not already exist on the
820 // global object add the property locally. We take special
821 // precautions to always add it as a local property even in case
822 // of callbacks in the prototype chain (this rules out using
823 // SetProperty). Also, we must use the handle-based version to
824 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000825 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000826 }
827 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000828
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000829 return Heap::undefined_value();
830}
831
832
833static Object* Runtime_DeclareContextSlot(Arguments args) {
834 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000835 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000836
ager@chromium.org7c537e22008-10-16 08:43:32 +0000837 CONVERT_ARG_CHECKED(Context, context, 0);
838 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000839 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000840 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000841 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000842 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000843
844 // Declarations are always done in the function context.
845 context = Handle<Context>(context->fcontext());
846
847 int index;
848 PropertyAttributes attributes;
849 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000850 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000851 context->Lookup(name, flags, &index, &attributes);
852
853 if (attributes != ABSENT) {
854 // The name was declared before; check for conflicting
855 // re-declarations: This is similar to the code in parser.cc in
856 // the AstBuildingParser::Declare function.
857 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
858 // Functions are not read-only.
859 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
860 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
861 return ThrowRedeclarationError(type, name);
862 }
863
864 // Initialize it if necessary.
865 if (*initial_value != NULL) {
866 if (index >= 0) {
867 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000868 // the function context or the arguments object.
869 if (holder->IsContext()) {
870 ASSERT(holder.is_identical_to(context));
871 if (((attributes & READ_ONLY) == 0) ||
872 context->get(index)->IsTheHole()) {
873 context->set(index, *initial_value);
874 }
875 } else {
876 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000877 }
878 } else {
879 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000880 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000881 SetProperty(context_ext, name, initial_value, mode);
882 }
883 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000886 // The property is not in the function context. It needs to be
887 // "declared" in the function context's extension context, or in the
888 // global context.
889 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000890 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000891 // The function context's extension context exists - use it.
892 context_ext = Handle<JSObject>(context->extension());
893 } else {
894 // The function context's extension context does not exists - allocate
895 // it.
896 context_ext = Factory::NewJSObject(Top::context_extension_function());
897 // And store it in the extension slot.
898 context->set_extension(*context_ext);
899 }
900 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000901
ager@chromium.org7c537e22008-10-16 08:43:32 +0000902 // Declare the property by setting it to the initial value if provided,
903 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
904 // constant declarations).
905 ASSERT(!context_ext->HasLocalProperty(*name));
906 Handle<Object> value(Heap::undefined_value());
907 if (*initial_value != NULL) value = initial_value;
908 SetProperty(context_ext, name, value, mode);
909 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
910 }
911
912 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000913}
914
915
916static Object* Runtime_InitializeVarGlobal(Arguments args) {
917 NoHandleAllocation nha;
918
919 // Determine if we need to assign to the variable if it already
920 // exists (based on the number of arguments).
921 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
922 bool assign = args.length() == 2;
923
924 CONVERT_ARG_CHECKED(String, name, 0);
925 GlobalObject* global = Top::context()->global();
926
927 // According to ECMA-262, section 12.2, page 62, the property must
928 // not be deletable.
929 PropertyAttributes attributes = DONT_DELETE;
930
931 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000932 // there, there is a property with this name in the prototype chain.
933 // We follow Safari and Firefox behavior and only set the property
934 // locally if there is an explicit initialization value that we have
935 // to assign to the property. When adding the property we take
936 // special precautions to always add it as a local property even in
937 // case of callbacks in the prototype chain (this rules out using
938 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
939 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000940 // Note that objects can have hidden prototypes, so we need to traverse
941 // the whole chain of hidden prototypes to do a 'local' lookup.
942 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000943 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000944 while (true) {
945 real_holder->LocalLookup(*name, &lookup);
946 if (lookup.IsProperty()) {
947 // Determine if this is a redeclaration of something read-only.
948 if (lookup.IsReadOnly()) {
949 // If we found readonly property on one of hidden prototypes,
950 // just shadow it.
951 if (real_holder != Top::context()->global()) break;
952 return ThrowRedeclarationError("const", name);
953 }
954
955 // Determine if this is a redeclaration of an intercepted read-only
956 // property and figure out if the property exists at all.
957 bool found = true;
958 PropertyType type = lookup.type();
959 if (type == INTERCEPTOR) {
960 HandleScope handle_scope;
961 Handle<JSObject> holder(real_holder);
962 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
963 real_holder = *holder;
964 if (intercepted == ABSENT) {
965 // The interceptor claims the property isn't there. We need to
966 // make sure to introduce it.
967 found = false;
968 } else if ((intercepted & READ_ONLY) != 0) {
969 // The property is present, but read-only. Since we're trying to
970 // overwrite it with a variable declaration we must throw a
971 // re-declaration error. However if we found readonly property
972 // on one of hidden prototypes, just shadow it.
973 if (real_holder != Top::context()->global()) break;
974 return ThrowRedeclarationError("const", name);
975 }
976 }
977
978 if (found && !assign) {
979 // The global property is there and we're not assigning any value
980 // to it. Just return.
981 return Heap::undefined_value();
982 }
983
984 // Assign the value (or undefined) to the property.
985 Object* value = (assign) ? args[1] : Heap::undefined_value();
986 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000987 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000988
989 Object* proto = real_holder->GetPrototype();
990 if (!proto->IsJSObject())
991 break;
992
993 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
994 break;
995
996 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000997 }
998
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000999 global = Top::context()->global();
1000 if (assign) {
1001 return global->IgnoreAttributesAndSetLocalProperty(*name,
1002 args[1],
1003 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001004 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001005 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001006}
1007
1008
1009static Object* Runtime_InitializeConstGlobal(Arguments args) {
1010 // All constants are declared with an initial value. The name
1011 // of the constant is the first argument and the initial value
1012 // is the second.
1013 RUNTIME_ASSERT(args.length() == 2);
1014 CONVERT_ARG_CHECKED(String, name, 0);
1015 Handle<Object> value = args.at<Object>(1);
1016
1017 // Get the current global object from top.
1018 GlobalObject* global = Top::context()->global();
1019
1020 // According to ECMA-262, section 12.2, page 62, the property must
1021 // not be deletable. Since it's a const, it must be READ_ONLY too.
1022 PropertyAttributes attributes =
1023 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1024
1025 // Lookup the property locally in the global object. If it isn't
1026 // there, we add the property and take special precautions to always
1027 // add it as a local property even in case of callbacks in the
1028 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001029 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001030 LookupResult lookup;
1031 global->LocalLookup(*name, &lookup);
1032 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001033 return global->IgnoreAttributesAndSetLocalProperty(*name,
1034 *value,
1035 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001036 }
1037
1038 // Determine if this is a redeclaration of something not
1039 // read-only. In case the result is hidden behind an interceptor we
1040 // need to ask it for the property attributes.
1041 if (!lookup.IsReadOnly()) {
1042 if (lookup.type() != INTERCEPTOR) {
1043 return ThrowRedeclarationError("var", name);
1044 }
1045
1046 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1047
1048 // Throw re-declaration error if the intercepted property is present
1049 // but not read-only.
1050 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1051 return ThrowRedeclarationError("var", name);
1052 }
1053
1054 // Restore global object from context (in case of GC) and continue
1055 // with setting the value because the property is either absent or
1056 // read-only. We also have to do redo the lookup.
1057 global = Top::context()->global();
1058
1059 // BUG 1213579: Handle the case where we have to set a read-only
1060 // property through an interceptor and only do it if it's
1061 // uninitialized, e.g. the hole. Nirk...
1062 global->SetProperty(*name, *value, attributes);
1063 return *value;
1064 }
1065
1066 // Set the value, but only we're assigning the initial value to a
1067 // constant. For now, we determine this by checking if the
1068 // current value is the hole.
1069 PropertyType type = lookup.type();
1070 if (type == FIELD) {
1071 FixedArray* properties = global->properties();
1072 int index = lookup.GetFieldIndex();
1073 if (properties->get(index)->IsTheHole()) {
1074 properties->set(index, *value);
1075 }
1076 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001077 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1078 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001079 }
1080 } else {
1081 // Ignore re-initialization of constants that have already been
1082 // assigned a function value.
1083 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1084 }
1085
1086 // Use the set value as the result of the operation.
1087 return *value;
1088}
1089
1090
1091static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1092 HandleScope scope;
1093 ASSERT(args.length() == 3);
1094
1095 Handle<Object> value(args[0]);
1096 ASSERT(!value->IsTheHole());
1097 CONVERT_ARG_CHECKED(Context, context, 1);
1098 Handle<String> name(String::cast(args[2]));
1099
1100 // Initializations are always done in the function context.
1101 context = Handle<Context>(context->fcontext());
1102
1103 int index;
1104 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001105 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001106 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001107 context->Lookup(name, flags, &index, &attributes);
1108
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001109 // In most situations, the property introduced by the const
1110 // declaration should be present in the context extension object.
1111 // However, because declaration and initialization are separate, the
1112 // property might have been deleted (if it was introduced by eval)
1113 // before we reach the initialization point.
1114 //
1115 // Example:
1116 //
1117 // function f() { eval("delete x; const x;"); }
1118 //
1119 // In that case, the initialization behaves like a normal assignment
1120 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001121 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001122 // Property was found in a context.
1123 if (holder->IsContext()) {
1124 // The holder cannot be the function context. If it is, there
1125 // should have been a const redeclaration error when declaring
1126 // the const property.
1127 ASSERT(!holder.is_identical_to(context));
1128 if ((attributes & READ_ONLY) == 0) {
1129 Handle<Context>::cast(holder)->set(index, *value);
1130 }
1131 } else {
1132 // The holder is an arguments object.
1133 ASSERT((attributes & READ_ONLY) == 0);
1134 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001135 }
1136 return *value;
1137 }
1138
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001139 // The property could not be found, we introduce it in the global
1140 // context.
1141 if (attributes == ABSENT) {
1142 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1143 SetProperty(global, name, value, NONE);
1144 return *value;
1145 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001146
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001147 // The property was present in a context extension object.
1148 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001149
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001150 if (*context_ext == context->extension()) {
1151 // This is the property that was introduced by the const
1152 // declaration. Set it if it hasn't been set before. NOTE: We
1153 // cannot use GetProperty() to get the current value as it
1154 // 'unholes' the value.
1155 LookupResult lookup;
1156 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1157 ASSERT(lookup.IsProperty()); // the property was declared
1158 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1159
1160 PropertyType type = lookup.type();
1161 if (type == FIELD) {
1162 FixedArray* properties = context_ext->properties();
1163 int index = lookup.GetFieldIndex();
1164 if (properties->get(index)->IsTheHole()) {
1165 properties->set(index, *value);
1166 }
1167 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001168 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1169 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001170 }
1171 } else {
1172 // We should not reach here. Any real, named property should be
1173 // either a field or a dictionary slot.
1174 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001175 }
1176 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001177 // The property was found in a different context extension object.
1178 // Set it if it is not a read-only property.
1179 if ((attributes & READ_ONLY) == 0) {
1180 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1181 // Setting a property might throw an exception. Exceptions
1182 // are converted to empty handles in handle operations. We
1183 // need to convert back to exceptions here.
1184 if (set.is_null()) {
1185 ASSERT(Top::has_pending_exception());
1186 return Failure::Exception();
1187 }
1188 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001189 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001190
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001191 return *value;
1192}
1193
1194
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001195static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1196 Arguments args) {
1197 HandleScope scope;
1198 ASSERT(args.length() == 2);
1199 CONVERT_ARG_CHECKED(JSObject, object, 0);
1200 CONVERT_SMI_CHECKED(properties, args[1]);
1201 if (object->HasFastProperties()) {
1202 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1203 }
1204 return *object;
1205}
1206
1207
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001208static Object* Runtime_RegExpExec(Arguments args) {
1209 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001210 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001211 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1212 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001213 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001214 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001215 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001216 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001217 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001218 RUNTIME_ASSERT(index >= 0);
1219 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001220 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001221 Handle<Object> result = RegExpImpl::Exec(regexp,
1222 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001223 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001224 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001225 if (result.is_null()) return Failure::Exception();
1226 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001227}
1228
1229
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001230static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1231 HandleScope scope;
1232 ASSERT(args.length() == 1);
1233 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1234 // This is necessary to enable fast checks for absence of elements
1235 // on Array.prototype and below.
1236 prototype->set_elements(Heap::empty_fixed_array());
1237 return Smi::FromInt(0);
1238}
1239
1240
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001241static void SetCustomCallGenerator(Handle<JSFunction> function,
1242 CustomCallGenerator generator) {
1243 if (function->shared()->function_data()->IsUndefined()) {
1244 function->shared()->set_function_data(*FromCData(generator));
1245 }
1246}
1247
1248
1249static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1250 const char* name,
1251 Builtins::Name builtin_name,
1252 CustomCallGenerator generator = NULL) {
1253 Handle<String> key = Factory::LookupAsciiSymbol(name);
1254 Handle<Code> code(Builtins::builtin(builtin_name));
1255 Handle<JSFunction> optimized = Factory::NewFunction(key,
1256 JS_OBJECT_TYPE,
1257 JSObject::kHeaderSize,
1258 code,
1259 false);
1260 optimized->shared()->DontAdaptArguments();
1261 if (generator != NULL) {
1262 SetCustomCallGenerator(optimized, generator);
1263 }
1264 SetProperty(holder, key, optimized, NONE);
1265 return optimized;
1266}
1267
1268
1269static Object* CompileArrayPushCall(CallStubCompiler* compiler,
1270 Object* object,
1271 JSObject* holder,
1272 JSFunction* function,
1273 String* name,
1274 StubCompiler::CheckType check) {
1275 return compiler->CompileArrayPushCall(object, holder, function, name, check);
1276}
1277
1278
1279static Object* CompileArrayPopCall(CallStubCompiler* compiler,
1280 Object* object,
1281 JSObject* holder,
1282 JSFunction* function,
1283 String* name,
1284 StubCompiler::CheckType check) {
1285 return compiler->CompileArrayPopCall(object, holder, function, name, check);
1286}
1287
1288
1289static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1290 HandleScope scope;
1291 ASSERT(args.length() == 1);
1292 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1293
1294 InstallBuiltin(holder, "pop", Builtins::ArrayPop, CompileArrayPopCall);
1295 InstallBuiltin(holder, "push", Builtins::ArrayPush, CompileArrayPushCall);
1296 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1297 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1298 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1299 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001300 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001301
1302 return *holder;
1303}
1304
1305
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001306static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1307 HandleScope scope;
1308 ASSERT(args.length() == 4);
1309 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1310 int index = Smi::cast(args[1])->value();
1311 Handle<String> pattern = args.at<String>(2);
1312 Handle<String> flags = args.at<String>(3);
1313
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001314 // Get the RegExp function from the context in the literals array.
1315 // This is the RegExp function from the context in which the
1316 // function was created. We do not use the RegExp function from the
1317 // current global context because this might be the RegExp function
1318 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001319 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001320 Handle<JSFunction>(
1321 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001322 // Compute the regular expression literal.
1323 bool has_pending_exception;
1324 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001325 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1326 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001327 if (has_pending_exception) {
1328 ASSERT(Top::has_pending_exception());
1329 return Failure::Exception();
1330 }
1331 literals->set(index, *regexp);
1332 return *regexp;
1333}
1334
1335
1336static Object* Runtime_FunctionGetName(Arguments args) {
1337 NoHandleAllocation ha;
1338 ASSERT(args.length() == 1);
1339
1340 CONVERT_CHECKED(JSFunction, f, args[0]);
1341 return f->shared()->name();
1342}
1343
1344
ager@chromium.org236ad962008-09-25 09:45:57 +00001345static Object* Runtime_FunctionSetName(Arguments args) {
1346 NoHandleAllocation ha;
1347 ASSERT(args.length() == 2);
1348
1349 CONVERT_CHECKED(JSFunction, f, args[0]);
1350 CONVERT_CHECKED(String, name, args[1]);
1351 f->shared()->set_name(name);
1352 return Heap::undefined_value();
1353}
1354
1355
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001356static Object* Runtime_FunctionGetScript(Arguments args) {
1357 HandleScope scope;
1358 ASSERT(args.length() == 1);
1359
1360 CONVERT_CHECKED(JSFunction, fun, args[0]);
1361 Handle<Object> script = Handle<Object>(fun->shared()->script());
1362 if (!script->IsScript()) return Heap::undefined_value();
1363
1364 return *GetScriptWrapper(Handle<Script>::cast(script));
1365}
1366
1367
1368static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1369 NoHandleAllocation ha;
1370 ASSERT(args.length() == 1);
1371
1372 CONVERT_CHECKED(JSFunction, f, args[0]);
1373 return f->shared()->GetSourceCode();
1374}
1375
1376
1377static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1378 NoHandleAllocation ha;
1379 ASSERT(args.length() == 1);
1380
1381 CONVERT_CHECKED(JSFunction, fun, args[0]);
1382 int pos = fun->shared()->start_position();
1383 return Smi::FromInt(pos);
1384}
1385
1386
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001387static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1388 ASSERT(args.length() == 2);
1389
1390 CONVERT_CHECKED(JSFunction, fun, args[0]);
1391 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1392
1393 Code* code = fun->code();
1394 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1395
1396 Address pc = code->address() + offset;
1397 return Smi::FromInt(fun->code()->SourcePosition(pc));
1398}
1399
1400
1401
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001402static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1403 NoHandleAllocation ha;
1404 ASSERT(args.length() == 2);
1405
1406 CONVERT_CHECKED(JSFunction, fun, args[0]);
1407 CONVERT_CHECKED(String, name, args[1]);
1408 fun->SetInstanceClassName(name);
1409 return Heap::undefined_value();
1410}
1411
1412
1413static Object* Runtime_FunctionSetLength(Arguments args) {
1414 NoHandleAllocation ha;
1415 ASSERT(args.length() == 2);
1416
1417 CONVERT_CHECKED(JSFunction, fun, args[0]);
1418 CONVERT_CHECKED(Smi, length, args[1]);
1419 fun->shared()->set_length(length->value());
1420 return length;
1421}
1422
1423
1424static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001425 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001426 ASSERT(args.length() == 2);
1427
1428 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001429 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1430 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001431 return args[0]; // return TOS
1432}
1433
1434
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001435static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1436 NoHandleAllocation ha;
1437 ASSERT(args.length() == 1);
1438
1439 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001440 return f->shared()->IsApiFunction() ? Heap::true_value()
1441 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001442}
1443
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001444static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1445 NoHandleAllocation ha;
1446 ASSERT(args.length() == 1);
1447
1448 CONVERT_CHECKED(JSFunction, f, args[0]);
1449 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1450}
1451
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001453static Object* Runtime_SetCode(Arguments args) {
1454 HandleScope scope;
1455 ASSERT(args.length() == 2);
1456
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001457 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001458 Handle<Object> code = args.at<Object>(1);
1459
1460 Handle<Context> context(target->context());
1461
1462 if (!code->IsNull()) {
1463 RUNTIME_ASSERT(code->IsJSFunction());
1464 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001465 Handle<SharedFunctionInfo> shared(fun->shared());
1466 SetExpectedNofProperties(target, shared->expected_nof_properties());
1467
1468 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001469 return Failure::Exception();
1470 }
1471 // Set the code, formal parameter count, and the length of the target
1472 // function.
1473 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001474 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001475 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001476 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001477 // Set the source code of the target function to undefined.
1478 // SetCode is only used for built-in constructors like String,
1479 // Array, and Object, and some web code
1480 // doesn't like seeing source code for constructors.
1481 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001482 // Clear the optimization hints related to the compiled code as these are no
1483 // longer valid when the code is overwritten.
1484 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001485 context = Handle<Context>(fun->context());
1486
1487 // Make sure we get a fresh copy of the literal vector to avoid
1488 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001489 int number_of_literals = fun->NumberOfLiterals();
1490 Handle<FixedArray> literals =
1491 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001492 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001493 // Insert the object, regexp and array functions in the literals
1494 // array prefix. These are the functions that will be used when
1495 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001496 literals->set(JSFunction::kLiteralGlobalContextIndex,
1497 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001498 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001499 // It's okay to skip the write barrier here because the literals
1500 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001501 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001502 }
1503
1504 target->set_context(*context);
1505 return *target;
1506}
1507
1508
1509static Object* CharCodeAt(String* subject, Object* index) {
1510 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001511 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001512 // Flatten the string. If someone wants to get a char at an index
1513 // in a cons string, it is likely that more indices will be
1514 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001515 Object* flat = subject->TryFlatten();
1516 if (flat->IsFailure()) return flat;
1517 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001518 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001519 return Heap::nan_value();
1520 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001521 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001522}
1523
1524
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001525static Object* CharFromCode(Object* char_code) {
1526 uint32_t code;
1527 if (Array::IndexFromObject(char_code, &code)) {
1528 if (code <= 0xffff) {
1529 return Heap::LookupSingleCharacterStringFromCode(code);
1530 }
1531 }
1532 return Heap::empty_string();
1533}
1534
1535
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001536static Object* Runtime_StringCharCodeAt(Arguments args) {
1537 NoHandleAllocation ha;
1538 ASSERT(args.length() == 2);
1539
1540 CONVERT_CHECKED(String, subject, args[0]);
1541 Object* index = args[1];
1542 return CharCodeAt(subject, index);
1543}
1544
1545
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001546static Object* Runtime_StringCharAt(Arguments args) {
1547 NoHandleAllocation ha;
1548 ASSERT(args.length() == 2);
1549
1550 CONVERT_CHECKED(String, subject, args[0]);
1551 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001552 Object* code = CharCodeAt(subject, index);
1553 if (code == Heap::nan_value()) {
1554 return Heap::undefined_value();
1555 }
1556 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001557}
1558
1559
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001560static Object* Runtime_CharFromCode(Arguments args) {
1561 NoHandleAllocation ha;
1562 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001563 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001564}
1565
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001566// Forward declarations.
1567static const int kStringBuilderConcatHelperLengthBits = 11;
1568static const int kStringBuilderConcatHelperPositionBits = 19;
1569
1570template <typename schar>
1571static inline void StringBuilderConcatHelper(String*,
1572 schar*,
1573 FixedArray*,
1574 int);
1575
1576typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1577typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1578
1579class ReplacementStringBuilder {
1580 public:
1581 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1582 : subject_(subject),
1583 parts_(Factory::NewFixedArray(estimated_part_count)),
1584 part_count_(0),
1585 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001586 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001587 // Require a non-zero initial size. Ensures that doubling the size to
1588 // extend the array will work.
1589 ASSERT(estimated_part_count > 0);
1590 }
1591
1592 void EnsureCapacity(int elements) {
1593 int length = parts_->length();
1594 int required_length = part_count_ + elements;
1595 if (length < required_length) {
1596 int new_length = length;
1597 do {
1598 new_length *= 2;
1599 } while (new_length < required_length);
1600 Handle<FixedArray> extended_array =
1601 Factory::NewFixedArray(new_length);
1602 parts_->CopyTo(0, *extended_array, 0, part_count_);
1603 parts_ = extended_array;
1604 }
1605 }
1606
1607 void AddSubjectSlice(int from, int to) {
1608 ASSERT(from >= 0);
1609 int length = to - from;
1610 ASSERT(length > 0);
1611 // Can we encode the slice in 11 bits for length and 19 bits for
1612 // start position - as used by StringBuilderConcatHelper?
1613 if (StringBuilderSubstringLength::is_valid(length) &&
1614 StringBuilderSubstringPosition::is_valid(from)) {
1615 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1616 StringBuilderSubstringPosition::encode(from);
1617 AddElement(Smi::FromInt(encoded_slice));
1618 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001619 // Otherwise encode as two smis.
1620 AddElement(Smi::FromInt(-length));
1621 AddElement(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001622 }
1623 IncrementCharacterCount(length);
1624 }
1625
1626
1627 void AddString(Handle<String> string) {
1628 int length = string->length();
1629 ASSERT(length > 0);
1630 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001631 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001632 is_ascii_ = false;
1633 }
1634 IncrementCharacterCount(length);
1635 }
1636
1637
1638 Handle<String> ToString() {
1639 if (part_count_ == 0) {
1640 return Factory::empty_string();
1641 }
1642
1643 Handle<String> joined_string;
1644 if (is_ascii_) {
1645 joined_string = NewRawAsciiString(character_count_);
1646 AssertNoAllocation no_alloc;
1647 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1648 char* char_buffer = seq->GetChars();
1649 StringBuilderConcatHelper(*subject_,
1650 char_buffer,
1651 *parts_,
1652 part_count_);
1653 } else {
1654 // Non-ASCII.
1655 joined_string = NewRawTwoByteString(character_count_);
1656 AssertNoAllocation no_alloc;
1657 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1658 uc16* char_buffer = seq->GetChars();
1659 StringBuilderConcatHelper(*subject_,
1660 char_buffer,
1661 *parts_,
1662 part_count_);
1663 }
1664 return joined_string;
1665 }
1666
1667
1668 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001669 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001670 V8::FatalProcessOutOfMemory("String.replace result too large.");
1671 }
1672 character_count_ += by;
1673 }
1674
1675 private:
1676
1677 Handle<String> NewRawAsciiString(int size) {
1678 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1679 }
1680
1681
1682 Handle<String> NewRawTwoByteString(int size) {
1683 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1684 }
1685
1686
1687 void AddElement(Object* element) {
1688 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001689 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001690 parts_->set(part_count_, element);
1691 part_count_++;
1692 }
1693
1694 Handle<String> subject_;
1695 Handle<FixedArray> parts_;
1696 int part_count_;
1697 int character_count_;
1698 bool is_ascii_;
1699};
1700
1701
1702class CompiledReplacement {
1703 public:
1704 CompiledReplacement()
1705 : parts_(1), replacement_substrings_(0) {}
1706
1707 void Compile(Handle<String> replacement,
1708 int capture_count,
1709 int subject_length);
1710
1711 void Apply(ReplacementStringBuilder* builder,
1712 int match_from,
1713 int match_to,
1714 Handle<JSArray> last_match_info);
1715
1716 // Number of distinct parts of the replacement pattern.
1717 int parts() {
1718 return parts_.length();
1719 }
1720 private:
1721 enum PartType {
1722 SUBJECT_PREFIX = 1,
1723 SUBJECT_SUFFIX,
1724 SUBJECT_CAPTURE,
1725 REPLACEMENT_SUBSTRING,
1726 REPLACEMENT_STRING,
1727
1728 NUMBER_OF_PART_TYPES
1729 };
1730
1731 struct ReplacementPart {
1732 static inline ReplacementPart SubjectMatch() {
1733 return ReplacementPart(SUBJECT_CAPTURE, 0);
1734 }
1735 static inline ReplacementPart SubjectCapture(int capture_index) {
1736 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1737 }
1738 static inline ReplacementPart SubjectPrefix() {
1739 return ReplacementPart(SUBJECT_PREFIX, 0);
1740 }
1741 static inline ReplacementPart SubjectSuffix(int subject_length) {
1742 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1743 }
1744 static inline ReplacementPart ReplacementString() {
1745 return ReplacementPart(REPLACEMENT_STRING, 0);
1746 }
1747 static inline ReplacementPart ReplacementSubString(int from, int to) {
1748 ASSERT(from >= 0);
1749 ASSERT(to > from);
1750 return ReplacementPart(-from, to);
1751 }
1752
1753 // If tag <= 0 then it is the negation of a start index of a substring of
1754 // the replacement pattern, otherwise it's a value from PartType.
1755 ReplacementPart(int tag, int data)
1756 : tag(tag), data(data) {
1757 // Must be non-positive or a PartType value.
1758 ASSERT(tag < NUMBER_OF_PART_TYPES);
1759 }
1760 // Either a value of PartType or a non-positive number that is
1761 // the negation of an index into the replacement string.
1762 int tag;
1763 // The data value's interpretation depends on the value of tag:
1764 // tag == SUBJECT_PREFIX ||
1765 // tag == SUBJECT_SUFFIX: data is unused.
1766 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1767 // tag == REPLACEMENT_SUBSTRING ||
1768 // tag == REPLACEMENT_STRING: data is index into array of substrings
1769 // of the replacement string.
1770 // tag <= 0: Temporary representation of the substring of the replacement
1771 // string ranging over -tag .. data.
1772 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1773 // substring objects.
1774 int data;
1775 };
1776
1777 template<typename Char>
1778 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1779 Vector<Char> characters,
1780 int capture_count,
1781 int subject_length) {
1782 int length = characters.length();
1783 int last = 0;
1784 for (int i = 0; i < length; i++) {
1785 Char c = characters[i];
1786 if (c == '$') {
1787 int next_index = i + 1;
1788 if (next_index == length) { // No next character!
1789 break;
1790 }
1791 Char c2 = characters[next_index];
1792 switch (c2) {
1793 case '$':
1794 if (i > last) {
1795 // There is a substring before. Include the first "$".
1796 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1797 last = next_index + 1; // Continue after the second "$".
1798 } else {
1799 // Let the next substring start with the second "$".
1800 last = next_index;
1801 }
1802 i = next_index;
1803 break;
1804 case '`':
1805 if (i > last) {
1806 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1807 }
1808 parts->Add(ReplacementPart::SubjectPrefix());
1809 i = next_index;
1810 last = i + 1;
1811 break;
1812 case '\'':
1813 if (i > last) {
1814 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1815 }
1816 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1817 i = next_index;
1818 last = i + 1;
1819 break;
1820 case '&':
1821 if (i > last) {
1822 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1823 }
1824 parts->Add(ReplacementPart::SubjectMatch());
1825 i = next_index;
1826 last = i + 1;
1827 break;
1828 case '0':
1829 case '1':
1830 case '2':
1831 case '3':
1832 case '4':
1833 case '5':
1834 case '6':
1835 case '7':
1836 case '8':
1837 case '9': {
1838 int capture_ref = c2 - '0';
1839 if (capture_ref > capture_count) {
1840 i = next_index;
1841 continue;
1842 }
1843 int second_digit_index = next_index + 1;
1844 if (second_digit_index < length) {
1845 // Peek ahead to see if we have two digits.
1846 Char c3 = characters[second_digit_index];
1847 if ('0' <= c3 && c3 <= '9') { // Double digits.
1848 int double_digit_ref = capture_ref * 10 + c3 - '0';
1849 if (double_digit_ref <= capture_count) {
1850 next_index = second_digit_index;
1851 capture_ref = double_digit_ref;
1852 }
1853 }
1854 }
1855 if (capture_ref > 0) {
1856 if (i > last) {
1857 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1858 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001859 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001860 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1861 last = next_index + 1;
1862 }
1863 i = next_index;
1864 break;
1865 }
1866 default:
1867 i = next_index;
1868 break;
1869 }
1870 }
1871 }
1872 if (length > last) {
1873 if (last == 0) {
1874 parts->Add(ReplacementPart::ReplacementString());
1875 } else {
1876 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1877 }
1878 }
1879 }
1880
1881 ZoneList<ReplacementPart> parts_;
1882 ZoneList<Handle<String> > replacement_substrings_;
1883};
1884
1885
1886void CompiledReplacement::Compile(Handle<String> replacement,
1887 int capture_count,
1888 int subject_length) {
1889 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001890 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001891 AssertNoAllocation no_alloc;
1892 ParseReplacementPattern(&parts_,
1893 replacement->ToAsciiVector(),
1894 capture_count,
1895 subject_length);
1896 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001897 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001898 AssertNoAllocation no_alloc;
1899
1900 ParseReplacementPattern(&parts_,
1901 replacement->ToUC16Vector(),
1902 capture_count,
1903 subject_length);
1904 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001905 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001906 int substring_index = 0;
1907 for (int i = 0, n = parts_.length(); i < n; i++) {
1908 int tag = parts_[i].tag;
1909 if (tag <= 0) { // A replacement string slice.
1910 int from = -tag;
1911 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001912 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001913 parts_[i].tag = REPLACEMENT_SUBSTRING;
1914 parts_[i].data = substring_index;
1915 substring_index++;
1916 } else if (tag == REPLACEMENT_STRING) {
1917 replacement_substrings_.Add(replacement);
1918 parts_[i].data = substring_index;
1919 substring_index++;
1920 }
1921 }
1922}
1923
1924
1925void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1926 int match_from,
1927 int match_to,
1928 Handle<JSArray> last_match_info) {
1929 for (int i = 0, n = parts_.length(); i < n; i++) {
1930 ReplacementPart part = parts_[i];
1931 switch (part.tag) {
1932 case SUBJECT_PREFIX:
1933 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1934 break;
1935 case SUBJECT_SUFFIX: {
1936 int subject_length = part.data;
1937 if (match_to < subject_length) {
1938 builder->AddSubjectSlice(match_to, subject_length);
1939 }
1940 break;
1941 }
1942 case SUBJECT_CAPTURE: {
1943 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001944 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001945 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1946 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1947 if (from >= 0 && to > from) {
1948 builder->AddSubjectSlice(from, to);
1949 }
1950 break;
1951 }
1952 case REPLACEMENT_SUBSTRING:
1953 case REPLACEMENT_STRING:
1954 builder->AddString(replacement_substrings_[part.data]);
1955 break;
1956 default:
1957 UNREACHABLE();
1958 }
1959 }
1960}
1961
1962
1963
1964static Object* StringReplaceRegExpWithString(String* subject,
1965 JSRegExp* regexp,
1966 String* replacement,
1967 JSArray* last_match_info) {
1968 ASSERT(subject->IsFlat());
1969 ASSERT(replacement->IsFlat());
1970
1971 HandleScope handles;
1972
1973 int length = subject->length();
1974 Handle<String> subject_handle(subject);
1975 Handle<JSRegExp> regexp_handle(regexp);
1976 Handle<String> replacement_handle(replacement);
1977 Handle<JSArray> last_match_info_handle(last_match_info);
1978 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1979 subject_handle,
1980 0,
1981 last_match_info_handle);
1982 if (match.is_null()) {
1983 return Failure::Exception();
1984 }
1985 if (match->IsNull()) {
1986 return *subject_handle;
1987 }
1988
1989 int capture_count = regexp_handle->CaptureCount();
1990
1991 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001992 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001993 CompiledReplacement compiled_replacement;
1994 compiled_replacement.Compile(replacement_handle,
1995 capture_count,
1996 length);
1997
1998 bool is_global = regexp_handle->GetFlags().is_global();
1999
2000 // Guessing the number of parts that the final result string is built
2001 // from. Global regexps can match any number of times, so we guess
2002 // conservatively.
2003 int expected_parts =
2004 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2005 ReplacementStringBuilder builder(subject_handle, expected_parts);
2006
2007 // Index of end of last match.
2008 int prev = 0;
2009
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002010 // Number of parts added by compiled replacement plus preceeding
2011 // string and possibly suffix after last match. It is possible for
2012 // all components to use two elements when encoded as two smis.
2013 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002014 bool matched = true;
2015 do {
2016 ASSERT(last_match_info_handle->HasFastElements());
2017 // Increase the capacity of the builder before entering local handle-scope,
2018 // so its internal buffer can safely allocate a new handle if it grows.
2019 builder.EnsureCapacity(parts_added_per_loop);
2020
2021 HandleScope loop_scope;
2022 int start, end;
2023 {
2024 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002025 FixedArray* match_info_array =
2026 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002027
2028 ASSERT_EQ(capture_count * 2 + 2,
2029 RegExpImpl::GetLastCaptureCount(match_info_array));
2030 start = RegExpImpl::GetCapture(match_info_array, 0);
2031 end = RegExpImpl::GetCapture(match_info_array, 1);
2032 }
2033
2034 if (prev < start) {
2035 builder.AddSubjectSlice(prev, start);
2036 }
2037 compiled_replacement.Apply(&builder,
2038 start,
2039 end,
2040 last_match_info_handle);
2041 prev = end;
2042
2043 // Only continue checking for global regexps.
2044 if (!is_global) break;
2045
2046 // Continue from where the match ended, unless it was an empty match.
2047 int next = end;
2048 if (start == end) {
2049 next = end + 1;
2050 if (next > length) break;
2051 }
2052
2053 match = RegExpImpl::Exec(regexp_handle,
2054 subject_handle,
2055 next,
2056 last_match_info_handle);
2057 if (match.is_null()) {
2058 return Failure::Exception();
2059 }
2060 matched = !match->IsNull();
2061 } while (matched);
2062
2063 if (prev < length) {
2064 builder.AddSubjectSlice(prev, length);
2065 }
2066
2067 return *(builder.ToString());
2068}
2069
2070
2071static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2072 ASSERT(args.length() == 4);
2073
2074 CONVERT_CHECKED(String, subject, args[0]);
2075 if (!subject->IsFlat()) {
2076 Object* flat_subject = subject->TryFlatten();
2077 if (flat_subject->IsFailure()) {
2078 return flat_subject;
2079 }
2080 subject = String::cast(flat_subject);
2081 }
2082
2083 CONVERT_CHECKED(String, replacement, args[2]);
2084 if (!replacement->IsFlat()) {
2085 Object* flat_replacement = replacement->TryFlatten();
2086 if (flat_replacement->IsFailure()) {
2087 return flat_replacement;
2088 }
2089 replacement = String::cast(flat_replacement);
2090 }
2091
2092 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2093 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2094
2095 ASSERT(last_match_info->HasFastElements());
2096
2097 return StringReplaceRegExpWithString(subject,
2098 regexp,
2099 replacement,
2100 last_match_info);
2101}
2102
2103
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002104
ager@chromium.org7c537e22008-10-16 08:43:32 +00002105// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2106// limit, we can fix the size of tables.
2107static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002108// Reduce alphabet to this size.
2109static const int kBMAlphabetSize = 0x100;
2110// For patterns below this length, the skip length of Boyer-Moore is too short
2111// to compensate for the algorithmic overhead compared to simple brute force.
2112static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002113
ager@chromium.org7c537e22008-10-16 08:43:32 +00002114// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2115// shift. Only allows the last kBMMaxShift characters of the needle
2116// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002117class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002118 public:
2119 BMGoodSuffixBuffers() {}
2120 inline void init(int needle_length) {
2121 ASSERT(needle_length > 1);
2122 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2123 int len = needle_length - start;
2124 biased_suffixes_ = suffixes_ - start;
2125 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2126 for (int i = 0; i <= len; i++) {
2127 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002128 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002129 }
2130 inline int& suffix(int index) {
2131 ASSERT(biased_suffixes_ + index >= suffixes_);
2132 return biased_suffixes_[index];
2133 }
2134 inline int& shift(int index) {
2135 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2136 return biased_good_suffix_shift_[index];
2137 }
2138 private:
2139 int suffixes_[kBMMaxShift + 1];
2140 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002141 int* biased_suffixes_;
2142 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002143 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2144};
2145
2146// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002147static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002148static BMGoodSuffixBuffers bmgs_buffers;
2149
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002150// State of the string match tables.
2151// SIMPLE: No usable content in the buffers.
2152// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2153// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2154// Whenever starting with a new needle, one should call InitializeStringSearch
2155// to determine which search strategy to use, and in the case of a long-needle
2156// strategy, the call also initializes the algorithm to SIMPLE.
2157enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2158static StringSearchAlgorithm algorithm;
2159
2160
ager@chromium.org7c537e22008-10-16 08:43:32 +00002161// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002162template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002163static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2164 // Only preprocess at most kBMMaxShift last characters of pattern.
2165 int start = pattern.length() < kBMMaxShift ? 0
2166 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002167 // Run forwards to populate bad_char_table, so that *last* instance
2168 // of character equivalence class is the one registered.
2169 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002170 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2171 : kBMAlphabetSize;
2172 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002173 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002174 } else {
2175 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002176 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002177 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002178 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002179 for (int i = start; i < pattern.length() - 1; i++) {
2180 pchar c = pattern[i];
2181 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002182 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002183 }
2184}
2185
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002186
ager@chromium.org7c537e22008-10-16 08:43:32 +00002187template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002188static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002189 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002190 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002191 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002192 // Compute Good Suffix tables.
2193 bmgs_buffers.init(m);
2194
2195 bmgs_buffers.shift(m-1) = 1;
2196 bmgs_buffers.suffix(m) = m + 1;
2197 pchar last_char = pattern[m - 1];
2198 int suffix = m + 1;
2199 for (int i = m; i > start;) {
2200 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2201 if (bmgs_buffers.shift(suffix) == len) {
2202 bmgs_buffers.shift(suffix) = suffix - i;
2203 }
2204 suffix = bmgs_buffers.suffix(suffix);
2205 }
2206 i--;
2207 suffix--;
2208 bmgs_buffers.suffix(i) = suffix;
2209 if (suffix == m) {
2210 // No suffix to extend, so we check against last_char only.
2211 while (i > start && pattern[i - 1] != last_char) {
2212 if (bmgs_buffers.shift(m) == len) {
2213 bmgs_buffers.shift(m) = m - i;
2214 }
2215 i--;
2216 bmgs_buffers.suffix(i) = m;
2217 }
2218 if (i > start) {
2219 i--;
2220 suffix--;
2221 bmgs_buffers.suffix(i) = suffix;
2222 }
2223 }
2224 }
2225 if (suffix < m) {
2226 for (int i = start; i <= m; i++) {
2227 if (bmgs_buffers.shift(i) == len) {
2228 bmgs_buffers.shift(i) = suffix - start;
2229 }
2230 if (i == suffix) {
2231 suffix = bmgs_buffers.suffix(suffix);
2232 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002233 }
2234 }
2235}
2236
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002237
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002238template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002239static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002240 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002241 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002242 }
2243 if (sizeof(pchar) == 1) {
2244 if (char_code > String::kMaxAsciiCharCode) {
2245 return -1;
2246 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002247 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002248 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002249 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002250}
2251
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002252
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002253// Restricted simplified Boyer-Moore string matching.
2254// Uses only the bad-shift table of Boyer-Moore and only uses it
2255// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002256template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002257static int BoyerMooreHorspool(Vector<const schar> subject,
2258 Vector<const pchar> pattern,
2259 int start_index,
2260 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002261 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002262 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002263 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002264
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002265 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002266
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002267 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002268 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002269 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002270 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002271 // Perform search
2272 for (idx = start_index; idx <= n - m;) {
2273 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002274 int c;
2275 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002276 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002277 int shift = j - bc_occ;
2278 idx += shift;
2279 badness += 1 - shift; // at most zero, so badness cannot increase.
2280 if (idx > n - m) {
2281 *complete = true;
2282 return -1;
2283 }
2284 }
2285 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002286 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002287 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002288 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002289 return idx;
2290 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002291 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002292 // Badness increases by the number of characters we have
2293 // checked, and decreases by the number of characters we
2294 // can skip by shifting. It's a measure of how we are doing
2295 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002296 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002297 if (badness > 0) {
2298 *complete = false;
2299 return idx;
2300 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002301 }
2302 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002303 *complete = true;
2304 return -1;
2305}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002306
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002307
2308template <typename schar, typename pchar>
2309static int BoyerMooreIndexOf(Vector<const schar> subject,
2310 Vector<const pchar> pattern,
2311 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002312 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002313 int n = subject.length();
2314 int m = pattern.length();
2315 // Only preprocess at most kBMMaxShift last characters of pattern.
2316 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2317
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002318 pchar last_char = pattern[m - 1];
2319 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002320 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002321 int j = m - 1;
2322 schar c;
2323 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002324 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002325 idx += shift;
2326 if (idx > n - m) {
2327 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002328 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002329 }
2330 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2331 if (j < 0) {
2332 return idx;
2333 } else if (j < start) {
2334 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002335 // Fall back on BMH shift.
2336 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002337 } else {
2338 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002339 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002340 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002341 if (gs_shift > shift) {
2342 shift = gs_shift;
2343 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002344 idx += shift;
2345 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002346 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002347
2348 return -1;
2349}
2350
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002351
2352template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002353static inline int SingleCharIndexOf(Vector<const schar> string,
2354 schar pattern_char,
2355 int start_index) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002356 for (int i = start_index, n = string.length(); i < n; i++) {
2357 if (pattern_char == string[i]) {
2358 return i;
2359 }
2360 }
2361 return -1;
2362}
2363
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002364
2365template <typename schar>
2366static int SingleCharLastIndexOf(Vector<const schar> string,
2367 schar pattern_char,
2368 int start_index) {
2369 for (int i = start_index; i >= 0; i--) {
2370 if (pattern_char == string[i]) {
2371 return i;
2372 }
2373 }
2374 return -1;
2375}
2376
2377
ager@chromium.org7c537e22008-10-16 08:43:32 +00002378// Trivial string search for shorter strings.
2379// On return, if "complete" is set to true, the return value is the
2380// final result of searching for the patter in the subject.
2381// If "complete" is set to false, the return value is the index where
2382// further checking should start, i.e., it's guaranteed that the pattern
2383// does not occur at a position prior to the returned index.
2384template <typename pchar, typename schar>
2385static int SimpleIndexOf(Vector<const schar> subject,
2386 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002387 int idx,
2388 bool* complete) {
2389 // Badness is a count of how much work we have done. When we have
2390 // done enough work we decide it's probably worth switching to a better
2391 // algorithm.
2392 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002393
ager@chromium.org7c537e22008-10-16 08:43:32 +00002394 // We know our pattern is at least 2 characters, we cache the first so
2395 // the common case of the first character not matching is faster.
2396 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002397 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2398 badness++;
2399 if (badness > 0) {
2400 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002401 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002402 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002403 if (subject[i] != pattern_first_char) continue;
2404 int j = 1;
2405 do {
2406 if (pattern[j] != subject[i+j]) {
2407 break;
2408 }
2409 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002410 } while (j < pattern.length());
2411 if (j == pattern.length()) {
2412 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002413 return i;
2414 }
2415 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002416 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002417 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002418 return -1;
2419}
2420
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002421// Simple indexOf that never bails out. For short patterns only.
2422template <typename pchar, typename schar>
2423static int SimpleIndexOf(Vector<const schar> subject,
2424 Vector<const pchar> pattern,
2425 int idx) {
2426 pchar pattern_first_char = pattern[0];
2427 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2428 if (subject[i] != pattern_first_char) continue;
2429 int j = 1;
2430 do {
2431 if (pattern[j] != subject[i+j]) {
2432 break;
2433 }
2434 j++;
2435 } while (j < pattern.length());
2436 if (j == pattern.length()) {
2437 return i;
2438 }
2439 }
2440 return -1;
2441}
2442
2443
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002444// Strategy for searching for a string in another string.
2445enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002446
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002447
2448template <typename pchar>
2449static inline StringSearchStrategy InitializeStringSearch(
2450 Vector<const pchar> pat, bool ascii_subject) {
2451 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002452 // We have an ASCII haystack and a non-ASCII needle. Check if there
2453 // really is a non-ASCII character in the needle and bail out if there
2454 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002455 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002456 for (int i = 0; i < pat.length(); i++) {
2457 uc16 c = pat[i];
2458 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002459 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002460 }
2461 }
2462 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002463 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002464 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002465 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002466 algorithm = SIMPLE_SEARCH;
2467 return SEARCH_LONG;
2468}
2469
2470
2471// Dispatch long needle searches to different algorithms.
2472template <typename schar, typename pchar>
2473static int ComplexIndexOf(Vector<const schar> sub,
2474 Vector<const pchar> pat,
2475 int start_index) {
2476 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002477 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002478 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002479 int idx = start_index;
2480 switch (algorithm) {
2481 case SIMPLE_SEARCH:
2482 idx = SimpleIndexOf(sub, pat, idx, &complete);
2483 if (complete) return idx;
2484 BoyerMoorePopulateBadCharTable(pat);
2485 algorithm = BOYER_MOORE_HORSPOOL;
2486 // FALLTHROUGH.
2487 case BOYER_MOORE_HORSPOOL:
2488 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2489 if (complete) return idx;
2490 // Build the Good Suffix table and continue searching.
2491 BoyerMoorePopulateGoodSuffixTable(pat);
2492 algorithm = BOYER_MOORE;
2493 // FALLTHROUGH.
2494 case BOYER_MOORE:
2495 return BoyerMooreIndexOf(sub, pat, idx);
2496 }
2497 UNREACHABLE();
2498 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002499}
2500
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002501
2502// Dispatch to different search strategies for a single search.
2503// If searching multiple times on the same needle, the search
2504// strategy should only be computed once and then dispatch to different
2505// loops.
2506template <typename schar, typename pchar>
2507static int StringSearch(Vector<const schar> sub,
2508 Vector<const pchar> pat,
2509 int start_index) {
2510 bool ascii_subject = (sizeof(schar) == 1);
2511 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2512 switch (strategy) {
2513 case SEARCH_FAIL: return -1;
2514 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2515 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2516 }
2517 UNREACHABLE();
2518 return -1;
2519}
2520
2521
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522// Perform string match of pattern on subject, starting at start index.
2523// Caller must ensure that 0 <= start_index <= sub->length(),
2524// and should check that pat->length() + start_index <= sub->length()
2525int Runtime::StringMatch(Handle<String> sub,
2526 Handle<String> pat,
2527 int start_index) {
2528 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002529 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002530
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002531 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002532 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002533
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002534 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002535 if (start_index + pattern_length > subject_length) return -1;
2536
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002537 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002538 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002539 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002540
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002541 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002542 // character patterns linear search is necessary, so any smart
2543 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002544 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002545 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002546 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002547 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002548 if (pchar > String::kMaxAsciiCharCode) {
2549 return -1;
2550 }
2551 Vector<const char> ascii_vector =
2552 sub->ToAsciiVector().SubVector(start_index, subject_length);
2553 const void* pos = memchr(ascii_vector.start(),
2554 static_cast<const char>(pchar),
2555 static_cast<size_t>(ascii_vector.length()));
2556 if (pos == NULL) {
2557 return -1;
2558 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002559 return static_cast<int>(reinterpret_cast<const char*>(pos)
2560 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002562 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002563 }
2564
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002565 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002566 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002567 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002568
ager@chromium.org7c537e22008-10-16 08:43:32 +00002569 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2570 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002571 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002572 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002573 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002574 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002575 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002576 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002577 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002578 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002579 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002580 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002581 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002582 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002583}
2584
2585
2586static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002587 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002588 ASSERT(args.length() == 3);
2589
ager@chromium.org7c537e22008-10-16 08:43:32 +00002590 CONVERT_ARG_CHECKED(String, sub, 0);
2591 CONVERT_ARG_CHECKED(String, pat, 1);
2592
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002593 Object* index = args[2];
2594 uint32_t start_index;
2595 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2596
ager@chromium.org870a0b62008-11-04 11:43:05 +00002597 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002598 int position = Runtime::StringMatch(sub, pat, start_index);
2599 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002600}
2601
2602
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002603template <typename schar, typename pchar>
2604static int StringMatchBackwards(Vector<const schar> sub,
2605 Vector<const pchar> pat,
2606 int idx) {
2607 ASSERT(pat.length() >= 1);
2608 ASSERT(idx + pat.length() <= sub.length());
2609
2610 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2611 for (int i = 0; i < pat.length(); i++) {
2612 uc16 c = pat[i];
2613 if (c > String::kMaxAsciiCharCode) {
2614 return -1;
2615 }
2616 }
2617 }
2618
2619 pchar pattern_first_char = pat[0];
2620 for (int i = idx; i >= 0; i--) {
2621 if (sub[i] != pattern_first_char) continue;
2622 int j = 1;
2623 while (j < pat.length()) {
2624 if (pat[j] != sub[i+j]) {
2625 break;
2626 }
2627 j++;
2628 }
2629 if (j == pat.length()) {
2630 return i;
2631 }
2632 }
2633 return -1;
2634}
2635
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002636static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002637 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638 ASSERT(args.length() == 3);
2639
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002640 CONVERT_ARG_CHECKED(String, sub, 0);
2641 CONVERT_ARG_CHECKED(String, pat, 1);
2642
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002644 uint32_t start_index;
2645 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2646
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002647 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002648 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002649
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002650 if (start_index + pat_length > sub_length) {
2651 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002652 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002653
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002654 if (pat_length == 0) {
2655 return Smi::FromInt(start_index);
2656 }
2657
2658 if (!sub->IsFlat()) {
2659 FlattenString(sub);
2660 }
2661
2662 if (pat_length == 1) {
2663 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2664 if (sub->IsAsciiRepresentation()) {
2665 uc16 pchar = pat->Get(0);
2666 if (pchar > String::kMaxAsciiCharCode) {
2667 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002668 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002669 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2670 static_cast<char>(pat->Get(0)),
2671 start_index));
2672 } else {
2673 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2674 pat->Get(0),
2675 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002676 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002677 }
2678
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002679 if (!pat->IsFlat()) {
2680 FlattenString(pat);
2681 }
2682
2683 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2684
2685 int position = -1;
2686
2687 if (pat->IsAsciiRepresentation()) {
2688 Vector<const char> pat_vector = pat->ToAsciiVector();
2689 if (sub->IsAsciiRepresentation()) {
2690 position = StringMatchBackwards(sub->ToAsciiVector(),
2691 pat_vector,
2692 start_index);
2693 } else {
2694 position = StringMatchBackwards(sub->ToUC16Vector(),
2695 pat_vector,
2696 start_index);
2697 }
2698 } else {
2699 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2700 if (sub->IsAsciiRepresentation()) {
2701 position = StringMatchBackwards(sub->ToAsciiVector(),
2702 pat_vector,
2703 start_index);
2704 } else {
2705 position = StringMatchBackwards(sub->ToUC16Vector(),
2706 pat_vector,
2707 start_index);
2708 }
2709 }
2710
2711 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002712}
2713
2714
2715static Object* Runtime_StringLocaleCompare(Arguments args) {
2716 NoHandleAllocation ha;
2717 ASSERT(args.length() == 2);
2718
2719 CONVERT_CHECKED(String, str1, args[0]);
2720 CONVERT_CHECKED(String, str2, args[1]);
2721
2722 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002723 int str1_length = str1->length();
2724 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002725
2726 // Decide trivial cases without flattening.
2727 if (str1_length == 0) {
2728 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2729 return Smi::FromInt(-str2_length);
2730 } else {
2731 if (str2_length == 0) return Smi::FromInt(str1_length);
2732 }
2733
2734 int end = str1_length < str2_length ? str1_length : str2_length;
2735
2736 // No need to flatten if we are going to find the answer on the first
2737 // character. At this point we know there is at least one character
2738 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002739 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002740 if (d != 0) return Smi::FromInt(d);
2741
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002742 str1->TryFlatten();
2743 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002744
2745 static StringInputBuffer buf1;
2746 static StringInputBuffer buf2;
2747
2748 buf1.Reset(str1);
2749 buf2.Reset(str2);
2750
2751 for (int i = 0; i < end; i++) {
2752 uint16_t char1 = buf1.GetNext();
2753 uint16_t char2 = buf2.GetNext();
2754 if (char1 != char2) return Smi::FromInt(char1 - char2);
2755 }
2756
2757 return Smi::FromInt(str1_length - str2_length);
2758}
2759
2760
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002761static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002762 NoHandleAllocation ha;
2763 ASSERT(args.length() == 3);
2764
2765 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002766 Object* from = args[1];
2767 Object* to = args[2];
2768 int start, end;
2769 // We have a fast integer-only case here to avoid a conversion to double in
2770 // the common case where from and to are Smis.
2771 if (from->IsSmi() && to->IsSmi()) {
2772 start = Smi::cast(from)->value();
2773 end = Smi::cast(to)->value();
2774 } else {
2775 CONVERT_DOUBLE_CHECKED(from_number, from);
2776 CONVERT_DOUBLE_CHECKED(to_number, to);
2777 start = FastD2I(from_number);
2778 end = FastD2I(to_number);
2779 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780 RUNTIME_ASSERT(end >= start);
2781 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002782 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002783 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002784 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002785}
2786
2787
ager@chromium.org41826e72009-03-30 13:30:57 +00002788static Object* Runtime_StringMatch(Arguments args) {
2789 ASSERT_EQ(3, args.length());
2790
2791 CONVERT_ARG_CHECKED(String, subject, 0);
2792 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2793 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2794 HandleScope handles;
2795
2796 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2797
2798 if (match.is_null()) {
2799 return Failure::Exception();
2800 }
2801 if (match->IsNull()) {
2802 return Heap::null_value();
2803 }
2804 int length = subject->length();
2805
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002806 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002807 ZoneList<int> offsets(8);
2808 do {
2809 int start;
2810 int end;
2811 {
2812 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002813 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002814 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2815 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2816 }
2817 offsets.Add(start);
2818 offsets.Add(end);
2819 int index = start < end ? end : end + 1;
2820 if (index > length) break;
2821 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2822 if (match.is_null()) {
2823 return Failure::Exception();
2824 }
2825 } while (!match->IsNull());
2826 int matches = offsets.length() / 2;
2827 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2828 for (int i = 0; i < matches ; i++) {
2829 int from = offsets.at(i * 2);
2830 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002831 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002832 }
2833 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2834 result->set_length(Smi::FromInt(matches));
2835 return *result;
2836}
2837
2838
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002839static Object* Runtime_NumberToRadixString(Arguments args) {
2840 NoHandleAllocation ha;
2841 ASSERT(args.length() == 2);
2842
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002843 // Fast case where the result is a one character string.
2844 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2845 int value = Smi::cast(args[0])->value();
2846 int radix = Smi::cast(args[1])->value();
2847 if (value >= 0 && value < radix) {
2848 RUNTIME_ASSERT(radix <= 36);
2849 // Character array used for conversion.
2850 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2851 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2852 }
2853 }
2854
2855 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002856 CONVERT_DOUBLE_CHECKED(value, args[0]);
2857 if (isnan(value)) {
2858 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2859 }
2860 if (isinf(value)) {
2861 if (value < 0) {
2862 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2863 }
2864 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2865 }
2866 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2867 int radix = FastD2I(radix_number);
2868 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2869 char* str = DoubleToRadixCString(value, radix);
2870 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2871 DeleteArray(str);
2872 return result;
2873}
2874
2875
2876static Object* Runtime_NumberToFixed(Arguments args) {
2877 NoHandleAllocation ha;
2878 ASSERT(args.length() == 2);
2879
2880 CONVERT_DOUBLE_CHECKED(value, args[0]);
2881 if (isnan(value)) {
2882 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2883 }
2884 if (isinf(value)) {
2885 if (value < 0) {
2886 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2887 }
2888 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2889 }
2890 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2891 int f = FastD2I(f_number);
2892 RUNTIME_ASSERT(f >= 0);
2893 char* str = DoubleToFixedCString(value, f);
2894 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2895 DeleteArray(str);
2896 return res;
2897}
2898
2899
2900static Object* Runtime_NumberToExponential(Arguments args) {
2901 NoHandleAllocation ha;
2902 ASSERT(args.length() == 2);
2903
2904 CONVERT_DOUBLE_CHECKED(value, args[0]);
2905 if (isnan(value)) {
2906 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2907 }
2908 if (isinf(value)) {
2909 if (value < 0) {
2910 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2911 }
2912 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2913 }
2914 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2915 int f = FastD2I(f_number);
2916 RUNTIME_ASSERT(f >= -1 && f <= 20);
2917 char* str = DoubleToExponentialCString(value, f);
2918 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2919 DeleteArray(str);
2920 return res;
2921}
2922
2923
2924static Object* Runtime_NumberToPrecision(Arguments args) {
2925 NoHandleAllocation ha;
2926 ASSERT(args.length() == 2);
2927
2928 CONVERT_DOUBLE_CHECKED(value, args[0]);
2929 if (isnan(value)) {
2930 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2931 }
2932 if (isinf(value)) {
2933 if (value < 0) {
2934 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2935 }
2936 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2937 }
2938 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2939 int f = FastD2I(f_number);
2940 RUNTIME_ASSERT(f >= 1 && f <= 21);
2941 char* str = DoubleToPrecisionCString(value, f);
2942 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2943 DeleteArray(str);
2944 return res;
2945}
2946
2947
2948// Returns a single character string where first character equals
2949// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002950static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002951 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002952 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002953 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002954 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002955 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002956 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002957}
2958
2959
2960Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2961 // Handle [] indexing on Strings
2962 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002963 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2964 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002965 }
2966
2967 // Handle [] indexing on String objects
2968 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002969 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2970 Handle<Object> result =
2971 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2972 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002973 }
2974
2975 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002976 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002977 return prototype->GetElement(index);
2978 }
2979
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002980 return GetElement(object, index);
2981}
2982
2983
2984Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002985 return object->GetElement(index);
2986}
2987
2988
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002989Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2990 HandleScope scope;
2991
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002992 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002993 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002994 Handle<Object> error =
2995 Factory::NewTypeError("non_object_property_load",
2996 HandleVector(args, 2));
2997 return Top::Throw(*error);
2998 }
2999
3000 // Check if the given key is an array index.
3001 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003002 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003003 return GetElementOrCharAt(object, index);
3004 }
3005
3006 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003007 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003008 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003009 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003010 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003011 bool has_pending_exception = false;
3012 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003013 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003014 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003015 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003016 }
3017
ager@chromium.org32912102009-01-16 10:38:43 +00003018 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003019 // the element if so.
3020 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003021 return GetElementOrCharAt(object, index);
3022 } else {
3023 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003024 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003025 }
3026}
3027
3028
3029static Object* Runtime_GetProperty(Arguments args) {
3030 NoHandleAllocation ha;
3031 ASSERT(args.length() == 2);
3032
3033 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003034 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003035
3036 return Runtime::GetObjectProperty(object, key);
3037}
3038
3039
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003040// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003041static Object* Runtime_KeyedGetProperty(Arguments args) {
3042 NoHandleAllocation ha;
3043 ASSERT(args.length() == 2);
3044
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003045 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003046 // itself.
3047 //
3048 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003049 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003050 // global proxy object never has properties. This is the case
3051 // because the global proxy object forwards everything to its hidden
3052 // prototype including local lookups.
3053 //
3054 // Additionally, we need to make sure that we do not cache results
3055 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003056 if (args[0]->IsJSObject() &&
3057 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003058 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003059 args[1]->IsString()) {
3060 JSObject* receiver = JSObject::cast(args[0]);
3061 String* key = String::cast(args[1]);
3062 if (receiver->HasFastProperties()) {
3063 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003064 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003065 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3066 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003067 Object* value = receiver->FastPropertyAt(offset);
3068 return value->IsTheHole() ? Heap::undefined_value() : value;
3069 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003070 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003071 LookupResult result;
3072 receiver->LocalLookup(key, &result);
3073 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
3074 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003075 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003076 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003077 }
3078 } else {
3079 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003080 StringDictionary* dictionary = receiver->property_dictionary();
3081 int entry = dictionary->FindEntry(key);
3082 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003083 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003084 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003085 if (!receiver->IsGlobalObject()) return value;
3086 value = JSGlobalPropertyCell::cast(value)->value();
3087 if (!value->IsTheHole()) return value;
3088 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003089 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003090 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003091 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3092 // Fast case for string indexing using [] with a smi index.
3093 HandleScope scope;
3094 Handle<String> str = args.at<String>(0);
3095 int index = Smi::cast(args[1])->value();
3096 Handle<Object> result = GetCharAt(str, index);
3097 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003098 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003099
3100 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003101 return Runtime::GetObjectProperty(args.at<Object>(0),
3102 args.at<Object>(1));
3103}
3104
3105
ager@chromium.org5c838252010-02-19 08:53:10 +00003106static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3107 ASSERT(args.length() == 5);
3108 HandleScope scope;
3109 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3110 CONVERT_CHECKED(String, name, args[1]);
3111 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3112 CONVERT_CHECKED(JSFunction, fun, args[3]);
3113 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3114 int unchecked = flag_attr->value();
3115 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3116 RUNTIME_ASSERT(!obj->IsNull());
3117 LookupResult result;
3118 obj->LocalLookupRealNamedProperty(name, &result);
3119
3120 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3121 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3122 // delete it to avoid running into trouble in DefineAccessor, which
3123 // handles this incorrectly if the property is readonly (does nothing)
3124 if (result.IsProperty() &&
3125 (result.type() == FIELD || result.type() == NORMAL
3126 || result.type() == CONSTANT_FUNCTION)) {
3127 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3128 }
3129 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3130}
3131
3132static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3133 ASSERT(args.length() == 4);
3134 HandleScope scope;
3135 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3136 CONVERT_ARG_CHECKED(String, name, 1);
3137 Handle<Object> obj_value = args.at<Object>(2);
3138
3139 CONVERT_CHECKED(Smi, flag, args[3]);
3140 int unchecked = flag->value();
3141 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3142
3143 LookupResult result;
3144 js_object->LocalLookupRealNamedProperty(*name, &result);
3145
3146 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3147
3148 // Take special care when attributes are different and there is already
3149 // a property. For simplicity we normalize the property which enables us
3150 // to not worry about changing the instance_descriptor and creating a new
3151 // map. The current version of SetObjectProperty does not handle attributes
3152 // correctly in the case where a property is a field and is reset with
3153 // new attributes.
3154 if (result.IsProperty() && attr != result.GetAttributes()) {
3155 // New attributes - normalize to avoid writing to instance descriptor
3156 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3157 // Use IgnoreAttributes version since a readonly property may be
3158 // overridden and SetProperty does not allow this.
3159 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3160 *obj_value,
3161 attr);
3162 }
3163 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3164}
3165
3166
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003167Object* Runtime::SetObjectProperty(Handle<Object> object,
3168 Handle<Object> key,
3169 Handle<Object> value,
3170 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003171 HandleScope scope;
3172
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003173 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003174 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003175 Handle<Object> error =
3176 Factory::NewTypeError("non_object_property_store",
3177 HandleVector(args, 2));
3178 return Top::Throw(*error);
3179 }
3180
3181 // If the object isn't a JavaScript object, we ignore the store.
3182 if (!object->IsJSObject()) return *value;
3183
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003184 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3185
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003186 // Check if the given key is an array index.
3187 uint32_t index;
3188 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003189 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3190 // of a string using [] notation. We need to support this too in
3191 // JavaScript.
3192 // In the case of a String object we just need to redirect the assignment to
3193 // the underlying string if the index is in range. Since the underlying
3194 // string does nothing with the assignment then we can ignore such
3195 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003196 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003197 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003198 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003199
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003200 Handle<Object> result = SetElement(js_object, index, value);
3201 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003202 return *value;
3203 }
3204
3205 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003206 Handle<Object> result;
3207 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003208 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003209 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003210 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003211 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003212 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003213 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003214 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003215 return *value;
3216 }
3217
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003218 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003219 bool has_pending_exception = false;
3220 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3221 if (has_pending_exception) return Failure::Exception();
3222 Handle<String> name = Handle<String>::cast(converted);
3223
3224 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003225 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003226 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003227 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003228 }
3229}
3230
3231
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003232Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3233 Handle<Object> key,
3234 Handle<Object> value,
3235 PropertyAttributes attr) {
3236 HandleScope scope;
3237
3238 // Check if the given key is an array index.
3239 uint32_t index;
3240 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003241 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3242 // of a string using [] notation. We need to support this too in
3243 // JavaScript.
3244 // In the case of a String object we just need to redirect the assignment to
3245 // the underlying string if the index is in range. Since the underlying
3246 // string does nothing with the assignment then we can ignore such
3247 // assignments.
3248 if (js_object->IsStringObjectWithCharacterAt(index)) {
3249 return *value;
3250 }
3251
3252 return js_object->SetElement(index, *value);
3253 }
3254
3255 if (key->IsString()) {
3256 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003257 return js_object->SetElement(index, *value);
3258 } else {
3259 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003260 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003261 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3262 *value,
3263 attr);
3264 }
3265 }
3266
3267 // Call-back into JavaScript to convert the key to a string.
3268 bool has_pending_exception = false;
3269 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3270 if (has_pending_exception) return Failure::Exception();
3271 Handle<String> name = Handle<String>::cast(converted);
3272
3273 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003274 return js_object->SetElement(index, *value);
3275 } else {
3276 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3277 }
3278}
3279
3280
ager@chromium.orge2902be2009-06-08 12:21:35 +00003281Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3282 Handle<Object> key) {
3283 HandleScope scope;
3284
3285 // Check if the given key is an array index.
3286 uint32_t index;
3287 if (Array::IndexFromObject(*key, &index)) {
3288 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3289 // characters of a string using [] notation. In the case of a
3290 // String object we just need to redirect the deletion to the
3291 // underlying string if the index is in range. Since the
3292 // underlying string does nothing with the deletion, we can ignore
3293 // such deletions.
3294 if (js_object->IsStringObjectWithCharacterAt(index)) {
3295 return Heap::true_value();
3296 }
3297
3298 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3299 }
3300
3301 Handle<String> key_string;
3302 if (key->IsString()) {
3303 key_string = Handle<String>::cast(key);
3304 } else {
3305 // Call-back into JavaScript to convert the key to a string.
3306 bool has_pending_exception = false;
3307 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3308 if (has_pending_exception) return Failure::Exception();
3309 key_string = Handle<String>::cast(converted);
3310 }
3311
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003312 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003313 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3314}
3315
3316
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003317static Object* Runtime_SetProperty(Arguments args) {
3318 NoHandleAllocation ha;
3319 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
3320
3321 Handle<Object> object = args.at<Object>(0);
3322 Handle<Object> key = args.at<Object>(1);
3323 Handle<Object> value = args.at<Object>(2);
3324
3325 // Compute attributes.
3326 PropertyAttributes attributes = NONE;
3327 if (args.length() == 4) {
3328 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003329 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003331 RUNTIME_ASSERT(
3332 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3333 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003334 }
3335 return Runtime::SetObjectProperty(object, key, value, attributes);
3336}
3337
3338
3339// Set a local property, even if it is READ_ONLY. If the property does not
3340// exist, it will be added with attributes NONE.
3341static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
3342 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003343 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003344 CONVERT_CHECKED(JSObject, object, args[0]);
3345 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003346 // Compute attributes.
3347 PropertyAttributes attributes = NONE;
3348 if (args.length() == 4) {
3349 CONVERT_CHECKED(Smi, value_obj, args[3]);
3350 int unchecked_value = value_obj->value();
3351 // Only attribute bits should be set.
3352 RUNTIME_ASSERT(
3353 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3354 attributes = static_cast<PropertyAttributes>(unchecked_value);
3355 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003356
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003357 return object->
3358 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003359}
3360
3361
3362static Object* Runtime_DeleteProperty(Arguments args) {
3363 NoHandleAllocation ha;
3364 ASSERT(args.length() == 2);
3365
3366 CONVERT_CHECKED(JSObject, object, args[0]);
3367 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00003368 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003369}
3370
3371
ager@chromium.org9085a012009-05-11 19:22:57 +00003372static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
3373 Handle<String> key) {
3374 if (object->HasLocalProperty(*key)) return Heap::true_value();
3375 // Handle hidden prototypes. If there's a hidden prototype above this thing
3376 // then we have to check it for properties, because they are supposed to
3377 // look like they are on this object.
3378 Handle<Object> proto(object->GetPrototype());
3379 if (proto->IsJSObject() &&
3380 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
3381 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
3382 }
3383 return Heap::false_value();
3384}
3385
3386
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003387static Object* Runtime_HasLocalProperty(Arguments args) {
3388 NoHandleAllocation ha;
3389 ASSERT(args.length() == 2);
3390 CONVERT_CHECKED(String, key, args[1]);
3391
ager@chromium.org9085a012009-05-11 19:22:57 +00003392 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003393 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00003394 if (obj->IsJSObject()) {
3395 JSObject* object = JSObject::cast(obj);
3396 // Fast case - no interceptors.
3397 if (object->HasRealNamedProperty(key)) return Heap::true_value();
3398 // Slow case. Either it's not there or we have an interceptor. We should
3399 // have handles for this kind of deal.
3400 HandleScope scope;
3401 return HasLocalPropertyImplementation(Handle<JSObject>(object),
3402 Handle<String>(key));
3403 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003404 // Well, there is one exception: Handle [] on strings.
3405 uint32_t index;
3406 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00003407 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003408 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003409 return Heap::true_value();
3410 }
3411 }
3412 return Heap::false_value();
3413}
3414
3415
3416static Object* Runtime_HasProperty(Arguments args) {
3417 NoHandleAllocation na;
3418 ASSERT(args.length() == 2);
3419
3420 // Only JS objects can have properties.
3421 if (args[0]->IsJSObject()) {
3422 JSObject* object = JSObject::cast(args[0]);
3423 CONVERT_CHECKED(String, key, args[1]);
3424 if (object->HasProperty(key)) return Heap::true_value();
3425 }
3426 return Heap::false_value();
3427}
3428
3429
3430static Object* Runtime_HasElement(Arguments args) {
3431 NoHandleAllocation na;
3432 ASSERT(args.length() == 2);
3433
3434 // Only JS objects can have elements.
3435 if (args[0]->IsJSObject()) {
3436 JSObject* object = JSObject::cast(args[0]);
3437 CONVERT_CHECKED(Smi, index_obj, args[1]);
3438 uint32_t index = index_obj->value();
3439 if (object->HasElement(index)) return Heap::true_value();
3440 }
3441 return Heap::false_value();
3442}
3443
3444
3445static Object* Runtime_IsPropertyEnumerable(Arguments args) {
3446 NoHandleAllocation ha;
3447 ASSERT(args.length() == 2);
3448
3449 CONVERT_CHECKED(JSObject, object, args[0]);
3450 CONVERT_CHECKED(String, key, args[1]);
3451
3452 uint32_t index;
3453 if (key->AsArrayIndex(&index)) {
3454 return Heap::ToBoolean(object->HasElement(index));
3455 }
3456
ager@chromium.org870a0b62008-11-04 11:43:05 +00003457 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
3458 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003459}
3460
3461
3462static Object* Runtime_GetPropertyNames(Arguments args) {
3463 HandleScope scope;
3464 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003465 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003466 return *GetKeysFor(object);
3467}
3468
3469
3470// Returns either a FixedArray as Runtime_GetPropertyNames,
3471// or, if the given object has an enum cache that contains
3472// all enumerable properties of the object and its prototypes
3473// have none, the map of the object. This is used to speed up
3474// the check for deletions during a for-in.
3475static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3476 ASSERT(args.length() == 1);
3477
3478 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3479
3480 if (raw_object->IsSimpleEnum()) return raw_object->map();
3481
3482 HandleScope scope;
3483 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003484 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3485 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003486
3487 // Test again, since cache may have been built by preceding call.
3488 if (object->IsSimpleEnum()) return object->map();
3489
3490 return *content;
3491}
3492
3493
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003494// Find the length of the prototype chain that is to to handled as one. If a
3495// prototype object is hidden it is to be viewed as part of the the object it
3496// is prototype for.
3497static int LocalPrototypeChainLength(JSObject* obj) {
3498 int count = 1;
3499 Object* proto = obj->GetPrototype();
3500 while (proto->IsJSObject() &&
3501 JSObject::cast(proto)->map()->is_hidden_prototype()) {
3502 count++;
3503 proto = JSObject::cast(proto)->GetPrototype();
3504 }
3505 return count;
3506}
3507
3508
3509// Return the names of the local named properties.
3510// args[0]: object
3511static Object* Runtime_GetLocalPropertyNames(Arguments args) {
3512 HandleScope scope;
3513 ASSERT(args.length() == 1);
3514 if (!args[0]->IsJSObject()) {
3515 return Heap::undefined_value();
3516 }
3517 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3518
3519 // Skip the global proxy as it has no properties and always delegates to the
3520 // real global object.
3521 if (obj->IsJSGlobalProxy()) {
3522 // Only collect names if access is permitted.
3523 if (obj->IsAccessCheckNeeded() &&
3524 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
3525 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
3526 return *Factory::NewJSArray(0);
3527 }
3528 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
3529 }
3530
3531 // Find the number of objects making up this.
3532 int length = LocalPrototypeChainLength(*obj);
3533
3534 // Find the number of local properties for each of the objects.
3535 int* local_property_count = NewArray<int>(length);
3536 int total_property_count = 0;
3537 Handle<JSObject> jsproto = obj;
3538 for (int i = 0; i < length; i++) {
3539 // Only collect names if access is permitted.
3540 if (jsproto->IsAccessCheckNeeded() &&
3541 !Top::MayNamedAccess(*jsproto,
3542 Heap::undefined_value(),
3543 v8::ACCESS_KEYS)) {
3544 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
3545 return *Factory::NewJSArray(0);
3546 }
3547 int n;
3548 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
3549 local_property_count[i] = n;
3550 total_property_count += n;
3551 if (i < length - 1) {
3552 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3553 }
3554 }
3555
3556 // Allocate an array with storage for all the property names.
3557 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
3558
3559 // Get the property names.
3560 jsproto = obj;
3561 int proto_with_hidden_properties = 0;
3562 for (int i = 0; i < length; i++) {
3563 jsproto->GetLocalPropertyNames(*names,
3564 i == 0 ? 0 : local_property_count[i - 1]);
3565 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
3566 proto_with_hidden_properties++;
3567 }
3568 if (i < length - 1) {
3569 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
3570 }
3571 }
3572
3573 // Filter out name of hidden propeties object.
3574 if (proto_with_hidden_properties > 0) {
3575 Handle<FixedArray> old_names = names;
3576 names = Factory::NewFixedArray(
3577 names->length() - proto_with_hidden_properties);
3578 int dest_pos = 0;
3579 for (int i = 0; i < total_property_count; i++) {
3580 Object* name = old_names->get(i);
3581 if (name == Heap::hidden_symbol()) {
3582 continue;
3583 }
3584 names->set(dest_pos++, name);
3585 }
3586 }
3587
3588 DeleteArray(local_property_count);
3589 return *Factory::NewJSArrayWithElements(names);
3590}
3591
3592
3593// Return the names of the local indexed properties.
3594// args[0]: object
3595static Object* Runtime_GetLocalElementNames(Arguments args) {
3596 HandleScope scope;
3597 ASSERT(args.length() == 1);
3598 if (!args[0]->IsJSObject()) {
3599 return Heap::undefined_value();
3600 }
3601 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3602
3603 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
3604 Handle<FixedArray> names = Factory::NewFixedArray(n);
3605 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
3606 return *Factory::NewJSArrayWithElements(names);
3607}
3608
3609
3610// Return information on whether an object has a named or indexed interceptor.
3611// args[0]: object
3612static Object* Runtime_GetInterceptorInfo(Arguments args) {
3613 HandleScope scope;
3614 ASSERT(args.length() == 1);
3615 if (!args[0]->IsJSObject()) {
3616 return Smi::FromInt(0);
3617 }
3618 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3619
3620 int result = 0;
3621 if (obj->HasNamedInterceptor()) result |= 2;
3622 if (obj->HasIndexedInterceptor()) result |= 1;
3623
3624 return Smi::FromInt(result);
3625}
3626
3627
3628// Return property names from named interceptor.
3629// args[0]: object
3630static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
3631 HandleScope scope;
3632 ASSERT(args.length() == 1);
3633 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3634
3635 if (obj->HasNamedInterceptor()) {
3636 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
3637 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3638 }
3639 return Heap::undefined_value();
3640}
3641
3642
3643// Return element names from indexed interceptor.
3644// args[0]: object
3645static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
3646 HandleScope scope;
3647 ASSERT(args.length() == 1);
3648 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3649
3650 if (obj->HasIndexedInterceptor()) {
3651 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
3652 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
3653 }
3654 return Heap::undefined_value();
3655}
3656
3657
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003658static Object* Runtime_LocalKeys(Arguments args) {
3659 ASSERT_EQ(args.length(), 1);
3660 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3661 HandleScope scope;
3662 Handle<JSObject> object(raw_object);
3663 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3664 LOCAL_ONLY);
3665 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3666 // property array and since the result is mutable we have to create
3667 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003668 int length = contents->length();
3669 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3670 for (int i = 0; i < length; i++) {
3671 Object* entry = contents->get(i);
3672 if (entry->IsString()) {
3673 copy->set(i, entry);
3674 } else {
3675 ASSERT(entry->IsNumber());
3676 HandleScope scope;
3677 Handle<Object> entry_handle(entry);
3678 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3679 copy->set(i, *entry_str);
3680 }
3681 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003682 return *Factory::NewJSArrayWithElements(copy);
3683}
3684
3685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003686static Object* Runtime_GetArgumentsProperty(Arguments args) {
3687 NoHandleAllocation ha;
3688 ASSERT(args.length() == 1);
3689
3690 // Compute the frame holding the arguments.
3691 JavaScriptFrameIterator it;
3692 it.AdvanceToArgumentsFrame();
3693 JavaScriptFrame* frame = it.frame();
3694
3695 // Get the actual number of provided arguments.
3696 const uint32_t n = frame->GetProvidedParametersCount();
3697
3698 // Try to convert the key to an index. If successful and within
3699 // index return the the argument from the frame.
3700 uint32_t index;
3701 if (Array::IndexFromObject(args[0], &index) && index < n) {
3702 return frame->GetParameter(index);
3703 }
3704
3705 // Convert the key to a string.
3706 HandleScope scope;
3707 bool exception = false;
3708 Handle<Object> converted =
3709 Execution::ToString(args.at<Object>(0), &exception);
3710 if (exception) return Failure::Exception();
3711 Handle<String> key = Handle<String>::cast(converted);
3712
3713 // Try to convert the string key into an array index.
3714 if (key->AsArrayIndex(&index)) {
3715 if (index < n) {
3716 return frame->GetParameter(index);
3717 } else {
3718 return Top::initial_object_prototype()->GetElement(index);
3719 }
3720 }
3721
3722 // Handle special arguments properties.
3723 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3724 if (key->Equals(Heap::callee_symbol())) return frame->function();
3725
3726 // Lookup in the initial Object.prototype object.
3727 return Top::initial_object_prototype()->GetProperty(*key);
3728}
3729
3730
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003731static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003732 HandleScope scope;
3733
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003734 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003735 Handle<Object> object = args.at<Object>(0);
3736 if (object->IsJSObject()) {
3737 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00003738 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
3739 js_object->TransformToFastProperties(0);
3740 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003741 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003742 return *object;
3743}
3744
3745
3746static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003747 HandleScope scope;
3748
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003749 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003750 Handle<Object> object = args.at<Object>(0);
3751 if (object->IsJSObject()) {
3752 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003753 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003754 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003755 return *object;
3756}
3757
3758
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003759static Object* Runtime_ToBool(Arguments args) {
3760 NoHandleAllocation ha;
3761 ASSERT(args.length() == 1);
3762
3763 return args[0]->ToBoolean();
3764}
3765
3766
3767// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3768// Possible optimizations: put the type string into the oddballs.
3769static Object* Runtime_Typeof(Arguments args) {
3770 NoHandleAllocation ha;
3771
3772 Object* obj = args[0];
3773 if (obj->IsNumber()) return Heap::number_symbol();
3774 HeapObject* heap_obj = HeapObject::cast(obj);
3775
3776 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003777 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003778
3779 InstanceType instance_type = heap_obj->map()->instance_type();
3780 if (instance_type < FIRST_NONSTRING_TYPE) {
3781 return Heap::string_symbol();
3782 }
3783
3784 switch (instance_type) {
3785 case ODDBALL_TYPE:
3786 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3787 return Heap::boolean_symbol();
3788 }
3789 if (heap_obj->IsNull()) {
3790 return Heap::object_symbol();
3791 }
3792 ASSERT(heap_obj->IsUndefined());
3793 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003794 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003795 return Heap::function_symbol();
3796 default:
3797 // For any kind of object not handled above, the spec rule for
3798 // host objects gives that it is okay to return "object"
3799 return Heap::object_symbol();
3800 }
3801}
3802
3803
3804static Object* Runtime_StringToNumber(Arguments args) {
3805 NoHandleAllocation ha;
3806 ASSERT(args.length() == 1);
3807 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003808 subject->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003809 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3810}
3811
3812
3813static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3814 NoHandleAllocation ha;
3815 ASSERT(args.length() == 1);
3816
3817 CONVERT_CHECKED(JSArray, codes, args[0]);
3818 int length = Smi::cast(codes->length())->value();
3819
3820 // Check if the string can be ASCII.
3821 int i;
3822 for (i = 0; i < length; i++) {
3823 Object* element = codes->GetElement(i);
3824 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3825 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3826 break;
3827 }
3828
3829 Object* object = NULL;
3830 if (i == length) { // The string is ASCII.
3831 object = Heap::AllocateRawAsciiString(length);
3832 } else { // The string is not ASCII.
3833 object = Heap::AllocateRawTwoByteString(length);
3834 }
3835
3836 if (object->IsFailure()) return object;
3837 String* result = String::cast(object);
3838 for (int i = 0; i < length; i++) {
3839 Object* element = codes->GetElement(i);
3840 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003841 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003842 }
3843 return result;
3844}
3845
3846
3847// kNotEscaped is generated by the following:
3848//
3849// #!/bin/perl
3850// for (my $i = 0; $i < 256; $i++) {
3851// print "\n" if $i % 16 == 0;
3852// my $c = chr($i);
3853// my $escaped = 1;
3854// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3855// print $escaped ? "0, " : "1, ";
3856// }
3857
3858
3859static bool IsNotEscaped(uint16_t character) {
3860 // Only for 8 bit characters, the rest are always escaped (in a different way)
3861 ASSERT(character < 256);
3862 static const char kNotEscaped[256] = {
3863 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3864 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3865 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3866 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3867 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3868 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3869 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3870 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3871 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3872 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3873 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3874 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3875 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3876 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3877 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3878 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3879 };
3880 return kNotEscaped[character] != 0;
3881}
3882
3883
3884static Object* Runtime_URIEscape(Arguments args) {
3885 const char hex_chars[] = "0123456789ABCDEF";
3886 NoHandleAllocation ha;
3887 ASSERT(args.length() == 1);
3888 CONVERT_CHECKED(String, source, args[0]);
3889
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003890 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003891
3892 int escaped_length = 0;
3893 int length = source->length();
3894 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003895 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003896 buffer->Reset(source);
3897 while (buffer->has_more()) {
3898 uint16_t character = buffer->GetNext();
3899 if (character >= 256) {
3900 escaped_length += 6;
3901 } else if (IsNotEscaped(character)) {
3902 escaped_length++;
3903 } else {
3904 escaped_length += 3;
3905 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003906 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003907 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003908 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003909 Top::context()->mark_out_of_memory();
3910 return Failure::OutOfMemoryException();
3911 }
3912 }
3913 }
3914 // No length change implies no change. Return original string if no change.
3915 if (escaped_length == length) {
3916 return source;
3917 }
3918 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3919 if (o->IsFailure()) return o;
3920 String* destination = String::cast(o);
3921 int dest_position = 0;
3922
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003923 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003924 buffer->Rewind();
3925 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003926 uint16_t chr = buffer->GetNext();
3927 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003928 destination->Set(dest_position, '%');
3929 destination->Set(dest_position+1, 'u');
3930 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3931 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3932 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3933 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003934 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003935 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003936 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003937 dest_position++;
3938 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003939 destination->Set(dest_position, '%');
3940 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3941 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003942 dest_position += 3;
3943 }
3944 }
3945 return destination;
3946}
3947
3948
3949static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3950 static const signed char kHexValue['g'] = {
3951 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3952 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3953 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3954 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3955 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3956 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3957 -1, 10, 11, 12, 13, 14, 15 };
3958
3959 if (character1 > 'f') return -1;
3960 int hi = kHexValue[character1];
3961 if (hi == -1) return -1;
3962 if (character2 > 'f') return -1;
3963 int lo = kHexValue[character2];
3964 if (lo == -1) return -1;
3965 return (hi << 4) + lo;
3966}
3967
3968
ager@chromium.org870a0b62008-11-04 11:43:05 +00003969static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003970 int i,
3971 int length,
3972 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003973 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003974 int32_t hi = 0;
3975 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003976 if (character == '%' &&
3977 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003978 source->Get(i + 1) == 'u' &&
3979 (hi = TwoDigitHex(source->Get(i + 2),
3980 source->Get(i + 3))) != -1 &&
3981 (lo = TwoDigitHex(source->Get(i + 4),
3982 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003983 *step = 6;
3984 return (hi << 8) + lo;
3985 } else if (character == '%' &&
3986 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003987 (lo = TwoDigitHex(source->Get(i + 1),
3988 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003989 *step = 3;
3990 return lo;
3991 } else {
3992 *step = 1;
3993 return character;
3994 }
3995}
3996
3997
3998static Object* Runtime_URIUnescape(Arguments args) {
3999 NoHandleAllocation ha;
4000 ASSERT(args.length() == 1);
4001 CONVERT_CHECKED(String, source, args[0]);
4002
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004003 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004004
4005 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004006 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004007
4008 int unescaped_length = 0;
4009 for (int i = 0; i < length; unescaped_length++) {
4010 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004011 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004012 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004013 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004014 i += step;
4015 }
4016
4017 // No length change implies no change. Return original string if no change.
4018 if (unescaped_length == length)
4019 return source;
4020
4021 Object* o = ascii ?
4022 Heap::AllocateRawAsciiString(unescaped_length) :
4023 Heap::AllocateRawTwoByteString(unescaped_length);
4024 if (o->IsFailure()) return o;
4025 String* destination = String::cast(o);
4026
4027 int dest_position = 0;
4028 for (int i = 0; i < length; dest_position++) {
4029 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004030 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031 i += step;
4032 }
4033 return destination;
4034}
4035
4036
4037static Object* Runtime_StringParseInt(Arguments args) {
4038 NoHandleAllocation ha;
4039
4040 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004041 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004042
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004043 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004044
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004045 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004046 int i;
4047
4048 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004049 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004050 if (i == len) return Heap::nan_value();
4051
4052 // Compute the sign (default to +).
4053 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004054 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004055 sign = -1;
4056 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004057 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004058 i++;
4059 }
4060
4061 // Compute the radix if 0.
4062 if (radix == 0) {
4063 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004064 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004065 radix = 8;
4066 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004067 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004068 if (c == 'x' || c == 'X') {
4069 radix = 16;
4070 i += 2;
4071 }
4072 }
4073 }
4074 } else if (radix == 16) {
4075 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004076 if (i + 1 < len && s->Get(i) == '0') {
4077 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004078 if (c == 'x' || c == 'X') i += 2;
4079 }
4080 }
4081
4082 RUNTIME_ASSERT(2 <= radix && radix <= 36);
4083 double value;
4084 int end_index = StringToInt(s, i, radix, &value);
4085 if (end_index != i) {
4086 return Heap::NumberFromDouble(sign * value);
4087 }
4088 return Heap::nan_value();
4089}
4090
4091
4092static Object* Runtime_StringParseFloat(Arguments args) {
4093 NoHandleAllocation ha;
4094 CONVERT_CHECKED(String, str, args[0]);
4095
4096 // ECMA-262 section 15.1.2.3, empty string is NaN
4097 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4098
4099 // Create a number object from the value.
4100 return Heap::NumberFromDouble(value);
4101}
4102
4103
4104static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4105static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4106
4107
4108template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004109static Object* ConvertCaseHelper(String* s,
4110 int length,
4111 int input_string_length,
4112 unibrow::Mapping<Converter, 128>* mapping) {
4113 // We try this twice, once with the assumption that the result is no longer
4114 // than the input and, if that assumption breaks, again with the exact
4115 // length. This may not be pretty, but it is nicer than what was here before
4116 // and I hereby claim my vaffel-is.
4117 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004118 // Allocate the resulting string.
4119 //
4120 // NOTE: This assumes that the upper/lower case of an ascii
4121 // character is also ascii. This is currently the case, but it
4122 // might break in the future if we implement more context and locale
4123 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004124 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004125 ? Heap::AllocateRawAsciiString(length)
4126 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004127 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004128 String* result = String::cast(o);
4129 bool has_changed_character = false;
4130
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004131 // Convert all characters to upper case, assuming that they will fit
4132 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004133 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004134 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004135 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004136 // We can assume that the string is not empty
4137 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004138 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004139 bool has_next = buffer->has_more();
4140 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004141 int char_length = mapping->get(current, next, chars);
4142 if (char_length == 0) {
4143 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004144 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004145 i++;
4146 } else if (char_length == 1) {
4147 // Common case: converting the letter resulted in one character.
4148 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004149 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004150 has_changed_character = true;
4151 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004152 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004153 // We've assumed that the result would be as long as the
4154 // input but here is a character that converts to several
4155 // characters. No matter, we calculate the exact length
4156 // of the result and try the whole thing again.
4157 //
4158 // Note that this leaves room for optimization. We could just
4159 // memcpy what we already have to the result string. Also,
4160 // the result string is the last object allocated we could
4161 // "realloc" it and probably, in the vast majority of cases,
4162 // extend the existing string to be able to hold the full
4163 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004164 int next_length = 0;
4165 if (has_next) {
4166 next_length = mapping->get(next, 0, chars);
4167 if (next_length == 0) next_length = 1;
4168 }
4169 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004170 while (buffer->has_more()) {
4171 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004172 // NOTE: we use 0 as the next character here because, while
4173 // the next character may affect what a character converts to,
4174 // it does not in any case affect the length of what it convert
4175 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004176 int char_length = mapping->get(current, 0, chars);
4177 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004178 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004179 if (current_length > Smi::kMaxValue) {
4180 Top::context()->mark_out_of_memory();
4181 return Failure::OutOfMemoryException();
4182 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004183 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004184 // Try again with the real length.
4185 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004186 } else {
4187 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004188 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004189 i++;
4190 }
4191 has_changed_character = true;
4192 }
4193 current = next;
4194 }
4195 if (has_changed_character) {
4196 return result;
4197 } else {
4198 // If we didn't actually change anything in doing the conversion
4199 // we simple return the result and let the converted string
4200 // become garbage; there is no reason to keep two identical strings
4201 // alive.
4202 return s;
4203 }
4204}
4205
4206
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004207static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4208 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4209 if (s->IsConsString()) {
4210 ASSERT(ConsString::cast(s)->second()->length() == 0);
4211 return SeqAsciiString::cast(ConsString::cast(s)->first());
4212 }
4213 return SeqAsciiString::cast(s);
4214}
4215
4216
4217namespace {
4218
4219struct ToLowerTraits {
4220 typedef unibrow::ToLowercase UnibrowConverter;
4221
4222 static bool ConvertAscii(char* dst, char* src, int length) {
4223 bool changed = false;
4224 for (int i = 0; i < length; ++i) {
4225 char c = src[i];
4226 if ('A' <= c && c <= 'Z') {
4227 c += ('a' - 'A');
4228 changed = true;
4229 }
4230 dst[i] = c;
4231 }
4232 return changed;
4233 }
4234};
4235
4236
4237struct ToUpperTraits {
4238 typedef unibrow::ToUppercase UnibrowConverter;
4239
4240 static bool ConvertAscii(char* dst, char* src, int length) {
4241 bool changed = false;
4242 for (int i = 0; i < length; ++i) {
4243 char c = src[i];
4244 if ('a' <= c && c <= 'z') {
4245 c -= ('a' - 'A');
4246 changed = true;
4247 }
4248 dst[i] = c;
4249 }
4250 return changed;
4251 }
4252};
4253
4254} // namespace
4255
4256
4257template <typename ConvertTraits>
4258static Object* ConvertCase(
4259 Arguments args,
4260 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004261 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004262 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004263 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004264
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004265 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004266 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004267 if (length == 0) return s;
4268
4269 // Simpler handling of ascii strings.
4270 //
4271 // NOTE: This assumes that the upper/lower case of an ascii
4272 // character is also ascii. This is currently the case, but it
4273 // might break in the future if we implement more context and locale
4274 // dependent upper/lower conversions.
4275 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4276 if (seq_ascii != NULL) {
4277 Object* o = Heap::AllocateRawAsciiString(length);
4278 if (o->IsFailure()) return o;
4279 SeqAsciiString* result = SeqAsciiString::cast(o);
4280 bool has_changed_character = ConvertTraits::ConvertAscii(
4281 result->GetChars(), seq_ascii->GetChars(), length);
4282 return has_changed_character ? result : s;
4283 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004284
4285 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4286 if (answer->IsSmi()) {
4287 // Retry with correct length.
4288 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4289 }
4290 return answer; // This may be a failure.
4291}
4292
4293
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004294static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004295 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004296}
4297
4298
4299static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004300 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004301}
4302
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004303
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004304static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
4305 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
4306}
4307
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004308
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004309static Object* Runtime_StringTrim(Arguments args) {
4310 NoHandleAllocation ha;
4311 ASSERT(args.length() == 3);
4312
4313 CONVERT_CHECKED(String, s, args[0]);
4314 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
4315 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
4316
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004317 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004318 int length = s->length();
4319
4320 int left = 0;
4321 if (trimLeft) {
4322 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
4323 left++;
4324 }
4325 }
4326
4327 int right = length;
4328 if (trimRight) {
4329 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
4330 right--;
4331 }
4332 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004333 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004334}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004335
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004336
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004337template <typename schar, typename pchar>
4338void FindStringIndices(Vector<const schar> subject,
4339 Vector<const pchar> pattern,
4340 ZoneList<int>* indices,
4341 unsigned int limit) {
4342 ASSERT(limit > 0);
4343 // Collect indices of pattern in subject, and the end-of-string index.
4344 // Stop after finding at most limit values.
4345 StringSearchStrategy strategy =
4346 InitializeStringSearch(pattern, sizeof(schar) == 1);
4347 switch (strategy) {
4348 case SEARCH_FAIL: return;
4349 case SEARCH_SHORT: {
4350 int pattern_length = pattern.length();
4351 int index = 0;
4352 while (limit > 0) {
4353 index = SimpleIndexOf(subject, pattern, index);
4354 if (index < 0) return;
4355 indices->Add(index);
4356 index += pattern_length;
4357 limit--;
4358 }
4359 return;
4360 }
4361 case SEARCH_LONG: {
4362 int pattern_length = pattern.length();
4363 int index = 0;
4364 while (limit > 0) {
4365 index = ComplexIndexOf(subject, pattern, index);
4366 if (index < 0) return;
4367 indices->Add(index);
4368 index += pattern_length;
4369 limit--;
4370 }
4371 return;
4372 }
4373 default:
4374 UNREACHABLE();
4375 return;
4376 }
4377}
4378
4379template <typename schar>
4380inline void FindCharIndices(Vector<const schar> subject,
4381 const schar pattern_char,
4382 ZoneList<int>* indices,
4383 unsigned int limit) {
4384 // Collect indices of pattern_char in subject, and the end-of-string index.
4385 // Stop after finding at most limit values.
4386 int index = 0;
4387 while (limit > 0) {
4388 index = SingleCharIndexOf(subject, pattern_char, index);
4389 if (index < 0) return;
4390 indices->Add(index);
4391 index++;
4392 limit--;
4393 }
4394}
4395
4396
4397static Object* Runtime_StringSplit(Arguments args) {
4398 ASSERT(args.length() == 3);
4399 HandleScope handle_scope;
4400 CONVERT_ARG_CHECKED(String, subject, 0);
4401 CONVERT_ARG_CHECKED(String, pattern, 1);
4402 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
4403
4404 int subject_length = subject->length();
4405 int pattern_length = pattern->length();
4406 RUNTIME_ASSERT(pattern_length > 0);
4407
4408 // The limit can be very large (0xffffffffu), but since the pattern
4409 // isn't empty, we can never create more parts than ~half the length
4410 // of the subject.
4411
4412 if (!subject->IsFlat()) FlattenString(subject);
4413
4414 static const int kMaxInitialListCapacity = 16;
4415
4416 ZoneScope scope(DELETE_ON_EXIT);
4417
4418 // Find (up to limit) indices of separator and end-of-string in subject
4419 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
4420 ZoneList<int> indices(initial_capacity);
4421 if (pattern_length == 1) {
4422 // Special case, go directly to fast single-character split.
4423 AssertNoAllocation nogc;
4424 uc16 pattern_char = pattern->Get(0);
4425 if (subject->IsTwoByteRepresentation()) {
4426 FindCharIndices(subject->ToUC16Vector(), pattern_char,
4427 &indices,
4428 limit);
4429 } else if (pattern_char <= String::kMaxAsciiCharCode) {
4430 FindCharIndices(subject->ToAsciiVector(),
4431 static_cast<char>(pattern_char),
4432 &indices,
4433 limit);
4434 }
4435 } else {
4436 if (!pattern->IsFlat()) FlattenString(pattern);
4437 AssertNoAllocation nogc;
4438 if (subject->IsAsciiRepresentation()) {
4439 Vector<const char> subject_vector = subject->ToAsciiVector();
4440 if (pattern->IsAsciiRepresentation()) {
4441 FindStringIndices(subject_vector,
4442 pattern->ToAsciiVector(),
4443 &indices,
4444 limit);
4445 } else {
4446 FindStringIndices(subject_vector,
4447 pattern->ToUC16Vector(),
4448 &indices,
4449 limit);
4450 }
4451 } else {
4452 Vector<const uc16> subject_vector = subject->ToUC16Vector();
4453 if (pattern->IsAsciiRepresentation()) {
4454 FindStringIndices(subject_vector,
4455 pattern->ToAsciiVector(),
4456 &indices,
4457 limit);
4458 } else {
4459 FindStringIndices(subject_vector,
4460 pattern->ToUC16Vector(),
4461 &indices,
4462 limit);
4463 }
4464 }
4465 }
4466 if (static_cast<uint32_t>(indices.length()) < limit) {
4467 indices.Add(subject_length);
4468 }
4469 // The list indices now contains the end of each part to create.
4470
4471
4472 // Create JSArray of substrings separated by separator.
4473 int part_count = indices.length();
4474
4475 Handle<JSArray> result = Factory::NewJSArray(part_count);
4476 result->set_length(Smi::FromInt(part_count));
4477
4478 ASSERT(result->HasFastElements());
4479
4480 if (part_count == 1 && indices.at(0) == subject_length) {
4481 FixedArray::cast(result->elements())->set(0, *subject);
4482 return *result;
4483 }
4484
4485 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
4486 int part_start = 0;
4487 for (int i = 0; i < part_count; i++) {
4488 HandleScope local_loop_handle;
4489 int part_end = indices.at(i);
4490 Handle<String> substring =
4491 Factory::NewSubString(subject, part_start, part_end);
4492 elements->set(i, *substring);
4493 part_start = part_end + pattern_length;
4494 }
4495
4496 return *result;
4497}
4498
4499
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004500// Copies ascii characters to the given fixed array looking up
4501// one-char strings in the cache. Gives up on the first char that is
4502// not in the cache and fills the remainder with smi zeros. Returns
4503// the length of the successfully copied prefix.
4504static int CopyCachedAsciiCharsToArray(const char* chars,
4505 FixedArray* elements,
4506 int length) {
4507 AssertNoAllocation nogc;
4508 FixedArray* ascii_cache = Heap::single_character_string_cache();
4509 Object* undefined = Heap::undefined_value();
4510 int i;
4511 for (i = 0; i < length; ++i) {
4512 Object* value = ascii_cache->get(chars[i]);
4513 if (value == undefined) break;
4514 ASSERT(!Heap::InNewSpace(value));
4515 elements->set(i, value, SKIP_WRITE_BARRIER);
4516 }
4517 if (i < length) {
4518 ASSERT(Smi::FromInt(0) == 0);
4519 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
4520 }
4521#ifdef DEBUG
4522 for (int j = 0; j < length; ++j) {
4523 Object* element = elements->get(j);
4524 ASSERT(element == Smi::FromInt(0) ||
4525 (element->IsString() && String::cast(element)->LooksValid()));
4526 }
4527#endif
4528 return i;
4529}
4530
4531
4532// Converts a String to JSArray.
4533// For example, "foo" => ["f", "o", "o"].
4534static Object* Runtime_StringToArray(Arguments args) {
4535 HandleScope scope;
4536 ASSERT(args.length() == 1);
4537 CONVERT_ARG_CHECKED(String, s, 0);
4538
4539 s->TryFlatten();
4540 const int length = s->length();
4541
4542 Handle<FixedArray> elements;
4543 if (s->IsFlat() && s->IsAsciiRepresentation()) {
4544 Object* obj = Heap::AllocateUninitializedFixedArray(length);
4545 if (obj->IsFailure()) return obj;
4546 elements = Handle<FixedArray>(FixedArray::cast(obj));
4547
4548 Vector<const char> chars = s->ToAsciiVector();
4549 // Note, this will initialize all elements (not only the prefix)
4550 // to prevent GC from seeing partially initialized array.
4551 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
4552 *elements,
4553 length);
4554
4555 for (int i = num_copied_from_cache; i < length; ++i) {
4556 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
4557 }
4558 } else {
4559 elements = Factory::NewFixedArray(length);
4560 for (int i = 0; i < length; ++i) {
4561 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
4562 }
4563 }
4564
4565#ifdef DEBUG
4566 for (int i = 0; i < length; ++i) {
4567 ASSERT(String::cast(elements->get(i))->length() == 1);
4568 }
4569#endif
4570
4571 return *Factory::NewJSArrayWithElements(elements);
4572}
4573
4574
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00004575bool Runtime::IsUpperCaseChar(uint16_t ch) {
4576 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
4577 int char_length = to_upper_mapping.get(ch, 0, chars);
4578 return char_length == 0;
4579}
4580
4581
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004582static Object* Runtime_NumberToString(Arguments args) {
4583 NoHandleAllocation ha;
4584 ASSERT(args.length() == 1);
4585
4586 Object* number = args[0];
4587 RUNTIME_ASSERT(number->IsNumber());
4588
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004589 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004590}
4591
4592
4593static Object* Runtime_NumberToInteger(Arguments args) {
4594 NoHandleAllocation ha;
4595 ASSERT(args.length() == 1);
4596
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004597 CONVERT_DOUBLE_CHECKED(number, args[0]);
4598
4599 // We do not include 0 so that we don't have to treat +0 / -0 cases.
4600 if (number > 0 && number <= Smi::kMaxValue) {
4601 return Smi::FromInt(static_cast<int>(number));
4602 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004603 return Heap::NumberFromDouble(DoubleToInteger(number));
4604}
4605
4606
4607static Object* Runtime_NumberToJSUint32(Arguments args) {
4608 NoHandleAllocation ha;
4609 ASSERT(args.length() == 1);
4610
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004611 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004612 return Heap::NumberFromUint32(number);
4613}
4614
4615
4616static Object* Runtime_NumberToJSInt32(Arguments args) {
4617 NoHandleAllocation ha;
4618 ASSERT(args.length() == 1);
4619
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004620 CONVERT_DOUBLE_CHECKED(number, args[0]);
4621
4622 // We do not include 0 so that we don't have to treat +0 / -0 cases.
4623 if (number > 0 && number <= Smi::kMaxValue) {
4624 return Smi::FromInt(static_cast<int>(number));
4625 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004626 return Heap::NumberFromInt32(DoubleToInt32(number));
4627}
4628
4629
ager@chromium.org870a0b62008-11-04 11:43:05 +00004630// Converts a Number to a Smi, if possible. Returns NaN if the number is not
4631// a small integer.
4632static Object* Runtime_NumberToSmi(Arguments args) {
4633 NoHandleAllocation ha;
4634 ASSERT(args.length() == 1);
4635
4636 Object* obj = args[0];
4637 if (obj->IsSmi()) {
4638 return obj;
4639 }
4640 if (obj->IsHeapNumber()) {
4641 double value = HeapNumber::cast(obj)->value();
4642 int int_value = FastD2I(value);
4643 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
4644 return Smi::FromInt(int_value);
4645 }
4646 }
4647 return Heap::nan_value();
4648}
4649
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004650
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004651static Object* Runtime_NumberAdd(Arguments args) {
4652 NoHandleAllocation ha;
4653 ASSERT(args.length() == 2);
4654
4655 CONVERT_DOUBLE_CHECKED(x, args[0]);
4656 CONVERT_DOUBLE_CHECKED(y, args[1]);
4657 return Heap::AllocateHeapNumber(x + y);
4658}
4659
4660
4661static Object* Runtime_NumberSub(Arguments args) {
4662 NoHandleAllocation ha;
4663 ASSERT(args.length() == 2);
4664
4665 CONVERT_DOUBLE_CHECKED(x, args[0]);
4666 CONVERT_DOUBLE_CHECKED(y, args[1]);
4667 return Heap::AllocateHeapNumber(x - y);
4668}
4669
4670
4671static Object* Runtime_NumberMul(Arguments args) {
4672 NoHandleAllocation ha;
4673 ASSERT(args.length() == 2);
4674
4675 CONVERT_DOUBLE_CHECKED(x, args[0]);
4676 CONVERT_DOUBLE_CHECKED(y, args[1]);
4677 return Heap::AllocateHeapNumber(x * y);
4678}
4679
4680
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004681static Object* Runtime_NumberUnaryMinus(Arguments args) {
4682 NoHandleAllocation ha;
4683 ASSERT(args.length() == 1);
4684
4685 CONVERT_DOUBLE_CHECKED(x, args[0]);
4686 return Heap::AllocateHeapNumber(-x);
4687}
4688
4689
4690static Object* Runtime_NumberDiv(Arguments args) {
4691 NoHandleAllocation ha;
4692 ASSERT(args.length() == 2);
4693
4694 CONVERT_DOUBLE_CHECKED(x, args[0]);
4695 CONVERT_DOUBLE_CHECKED(y, args[1]);
4696 return Heap::NewNumberFromDouble(x / y);
4697}
4698
4699
4700static Object* Runtime_NumberMod(Arguments args) {
4701 NoHandleAllocation ha;
4702 ASSERT(args.length() == 2);
4703
4704 CONVERT_DOUBLE_CHECKED(x, args[0]);
4705 CONVERT_DOUBLE_CHECKED(y, args[1]);
4706
ager@chromium.org3811b432009-10-28 14:53:37 +00004707 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004708 // NewNumberFromDouble may return a Smi instead of a Number object
4709 return Heap::NewNumberFromDouble(x);
4710}
4711
4712
4713static Object* Runtime_StringAdd(Arguments args) {
4714 NoHandleAllocation ha;
4715 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004716 CONVERT_CHECKED(String, str1, args[0]);
4717 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004718 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004719 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004720}
4721
4722
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004723template<typename sinkchar>
4724static inline void StringBuilderConcatHelper(String* special,
4725 sinkchar* sink,
4726 FixedArray* fixed_array,
4727 int array_length) {
4728 int position = 0;
4729 for (int i = 0; i < array_length; i++) {
4730 Object* element = fixed_array->get(i);
4731 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004732 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004733 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004734 int pos;
4735 int len;
4736 if (encoded_slice > 0) {
4737 // Position and length encoded in one smi.
4738 pos = StringBuilderSubstringPosition::decode(encoded_slice);
4739 len = StringBuilderSubstringLength::decode(encoded_slice);
4740 } else {
4741 // Position and length encoded in two smis.
4742 Object* obj = fixed_array->get(++i);
4743 ASSERT(obj->IsSmi());
4744 pos = Smi::cast(obj)->value();
4745 len = -encoded_slice;
4746 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00004747 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004748 sink + position,
4749 pos,
4750 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004751 position += len;
4752 } else {
4753 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004754 int element_length = string->length();
4755 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004756 position += element_length;
4757 }
4758 }
4759}
4760
4761
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004762static Object* Runtime_StringBuilderConcat(Arguments args) {
4763 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004764 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004766 if (!args[1]->IsSmi()) {
4767 Top::context()->mark_out_of_memory();
4768 return Failure::OutOfMemoryException();
4769 }
4770 int array_length = Smi::cast(args[1])->value();
4771 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004772
4773 // This assumption is used by the slice encoding in one or two smis.
4774 ASSERT(Smi::kMaxValue >= String::kMaxLength);
4775
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004776 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777 if (!array->HasFastElements()) {
4778 return Top::Throw(Heap::illegal_argument_symbol());
4779 }
4780 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004781 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004782 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004783 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004784
4785 if (array_length == 0) {
4786 return Heap::empty_string();
4787 } else if (array_length == 1) {
4788 Object* first = fixed_array->get(0);
4789 if (first->IsString()) return first;
4790 }
4791
ager@chromium.org5ec48922009-05-05 07:25:34 +00004792 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793 int position = 0;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004794 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004795 for (int i = 0; i < array_length; i++) {
4796 Object* elt = fixed_array->get(i);
4797 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004798 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004799 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004800 if (len > 0) {
4801 // Position and length encoded in one smi.
4802 int pos = len >> 11;
4803 len &= 0x7ff;
4804 if (pos + len > special_length) {
4805 return Top::Throw(Heap::illegal_argument_symbol());
4806 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004807 increment = len;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004808 } else {
4809 // Position and length encoded in two smis.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004810 increment = (-len);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004811 // Get the position and check that it is also a smi.
4812 i++;
4813 if (i >= array_length) {
4814 return Top::Throw(Heap::illegal_argument_symbol());
4815 }
4816 Object* pos = fixed_array->get(i);
4817 if (!pos->IsSmi()) {
4818 return Top::Throw(Heap::illegal_argument_symbol());
4819 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004821 } else if (elt->IsString()) {
4822 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004823 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004824 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004825 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004826 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004827 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004828 } else {
4829 return Top::Throw(Heap::illegal_argument_symbol());
4830 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004831 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004832 Top::context()->mark_out_of_memory();
4833 return Failure::OutOfMemoryException();
4834 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004835 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004836 }
4837
4838 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004839 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004840
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004841 if (ascii) {
4842 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004843 if (object->IsFailure()) return object;
4844 SeqAsciiString* answer = SeqAsciiString::cast(object);
4845 StringBuilderConcatHelper(special,
4846 answer->GetChars(),
4847 fixed_array,
4848 array_length);
4849 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004850 } else {
4851 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004852 if (object->IsFailure()) return object;
4853 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
4854 StringBuilderConcatHelper(special,
4855 answer->GetChars(),
4856 fixed_array,
4857 array_length);
4858 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004859 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860}
4861
4862
4863static Object* Runtime_NumberOr(Arguments args) {
4864 NoHandleAllocation ha;
4865 ASSERT(args.length() == 2);
4866
4867 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4868 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4869 return Heap::NumberFromInt32(x | y);
4870}
4871
4872
4873static Object* Runtime_NumberAnd(Arguments args) {
4874 NoHandleAllocation ha;
4875 ASSERT(args.length() == 2);
4876
4877 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4878 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4879 return Heap::NumberFromInt32(x & y);
4880}
4881
4882
4883static Object* Runtime_NumberXor(Arguments args) {
4884 NoHandleAllocation ha;
4885 ASSERT(args.length() == 2);
4886
4887 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4888 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4889 return Heap::NumberFromInt32(x ^ y);
4890}
4891
4892
4893static Object* Runtime_NumberNot(Arguments args) {
4894 NoHandleAllocation ha;
4895 ASSERT(args.length() == 1);
4896
4897 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4898 return Heap::NumberFromInt32(~x);
4899}
4900
4901
4902static Object* Runtime_NumberShl(Arguments args) {
4903 NoHandleAllocation ha;
4904 ASSERT(args.length() == 2);
4905
4906 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4907 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4908 return Heap::NumberFromInt32(x << (y & 0x1f));
4909}
4910
4911
4912static Object* Runtime_NumberShr(Arguments args) {
4913 NoHandleAllocation ha;
4914 ASSERT(args.length() == 2);
4915
4916 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
4917 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4918 return Heap::NumberFromUint32(x >> (y & 0x1f));
4919}
4920
4921
4922static Object* Runtime_NumberSar(Arguments args) {
4923 NoHandleAllocation ha;
4924 ASSERT(args.length() == 2);
4925
4926 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
4927 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
4928 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
4929}
4930
4931
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004932static Object* Runtime_NumberEquals(Arguments args) {
4933 NoHandleAllocation ha;
4934 ASSERT(args.length() == 2);
4935
4936 CONVERT_DOUBLE_CHECKED(x, args[0]);
4937 CONVERT_DOUBLE_CHECKED(y, args[1]);
4938 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
4939 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
4940 if (x == y) return Smi::FromInt(EQUAL);
4941 Object* result;
4942 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
4943 result = Smi::FromInt(EQUAL);
4944 } else {
4945 result = Smi::FromInt(NOT_EQUAL);
4946 }
4947 return result;
4948}
4949
4950
4951static Object* Runtime_StringEquals(Arguments args) {
4952 NoHandleAllocation ha;
4953 ASSERT(args.length() == 2);
4954
4955 CONVERT_CHECKED(String, x, args[0]);
4956 CONVERT_CHECKED(String, y, args[1]);
4957
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004958 bool not_equal = !x->Equals(y);
4959 // This is slightly convoluted because the value that signifies
4960 // equality is 0 and inequality is 1 so we have to negate the result
4961 // from String::Equals.
4962 ASSERT(not_equal == 0 || not_equal == 1);
4963 STATIC_CHECK(EQUAL == 0);
4964 STATIC_CHECK(NOT_EQUAL == 1);
4965 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004966}
4967
4968
4969static Object* Runtime_NumberCompare(Arguments args) {
4970 NoHandleAllocation ha;
4971 ASSERT(args.length() == 3);
4972
4973 CONVERT_DOUBLE_CHECKED(x, args[0]);
4974 CONVERT_DOUBLE_CHECKED(y, args[1]);
4975 if (isnan(x) || isnan(y)) return args[2];
4976 if (x == y) return Smi::FromInt(EQUAL);
4977 if (isless(x, y)) return Smi::FromInt(LESS);
4978 return Smi::FromInt(GREATER);
4979}
4980
4981
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004982// Compare two Smis as if they were converted to strings and then
4983// compared lexicographically.
4984static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4985 NoHandleAllocation ha;
4986 ASSERT(args.length() == 2);
4987
4988 // Arrays for the individual characters of the two Smis. Smis are
4989 // 31 bit integers and 10 decimal digits are therefore enough.
4990 static int x_elms[10];
4991 static int y_elms[10];
4992
4993 // Extract the integer values from the Smis.
4994 CONVERT_CHECKED(Smi, x, args[0]);
4995 CONVERT_CHECKED(Smi, y, args[1]);
4996 int x_value = x->value();
4997 int y_value = y->value();
4998
4999 // If the integers are equal so are the string representations.
5000 if (x_value == y_value) return Smi::FromInt(EQUAL);
5001
5002 // If one of the integers are zero the normal integer order is the
5003 // same as the lexicographic order of the string representations.
5004 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5005
ager@chromium.org32912102009-01-16 10:38:43 +00005006 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005007 // smallest because the char code of '-' is less than the char code
5008 // of any digit. Otherwise, we make both values positive.
5009 if (x_value < 0 || y_value < 0) {
5010 if (y_value >= 0) return Smi::FromInt(LESS);
5011 if (x_value >= 0) return Smi::FromInt(GREATER);
5012 x_value = -x_value;
5013 y_value = -y_value;
5014 }
5015
5016 // Convert the integers to arrays of their decimal digits.
5017 int x_index = 0;
5018 int y_index = 0;
5019 while (x_value > 0) {
5020 x_elms[x_index++] = x_value % 10;
5021 x_value /= 10;
5022 }
5023 while (y_value > 0) {
5024 y_elms[y_index++] = y_value % 10;
5025 y_value /= 10;
5026 }
5027
5028 // Loop through the arrays of decimal digits finding the first place
5029 // where they differ.
5030 while (--x_index >= 0 && --y_index >= 0) {
5031 int diff = x_elms[x_index] - y_elms[y_index];
5032 if (diff != 0) return Smi::FromInt(diff);
5033 }
5034
5035 // If one array is a suffix of the other array, the longest array is
5036 // the representation of the largest of the Smis in the
5037 // lexicographic ordering.
5038 return Smi::FromInt(x_index - y_index);
5039}
5040
5041
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005042static Object* StringInputBufferCompare(String* x, String* y) {
5043 static StringInputBuffer bufx;
5044 static StringInputBuffer bufy;
5045 bufx.Reset(x);
5046 bufy.Reset(y);
5047 while (bufx.has_more() && bufy.has_more()) {
5048 int d = bufx.GetNext() - bufy.GetNext();
5049 if (d < 0) return Smi::FromInt(LESS);
5050 else if (d > 0) return Smi::FromInt(GREATER);
5051 }
5052
5053 // x is (non-trivial) prefix of y:
5054 if (bufy.has_more()) return Smi::FromInt(LESS);
5055 // y is prefix of x:
5056 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5057}
5058
5059
5060static Object* FlatStringCompare(String* x, String* y) {
5061 ASSERT(x->IsFlat());
5062 ASSERT(y->IsFlat());
5063 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5064 int prefix_length = x->length();
5065 if (y->length() < prefix_length) {
5066 prefix_length = y->length();
5067 equal_prefix_result = Smi::FromInt(GREATER);
5068 } else if (y->length() > prefix_length) {
5069 equal_prefix_result = Smi::FromInt(LESS);
5070 }
5071 int r;
5072 if (x->IsAsciiRepresentation()) {
5073 Vector<const char> x_chars = x->ToAsciiVector();
5074 if (y->IsAsciiRepresentation()) {
5075 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005076 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005077 } else {
5078 Vector<const uc16> y_chars = y->ToUC16Vector();
5079 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5080 }
5081 } else {
5082 Vector<const uc16> x_chars = x->ToUC16Vector();
5083 if (y->IsAsciiRepresentation()) {
5084 Vector<const char> y_chars = y->ToAsciiVector();
5085 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5086 } else {
5087 Vector<const uc16> y_chars = y->ToUC16Vector();
5088 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5089 }
5090 }
5091 Object* result;
5092 if (r == 0) {
5093 result = equal_prefix_result;
5094 } else {
5095 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5096 }
5097 ASSERT(result == StringInputBufferCompare(x, y));
5098 return result;
5099}
5100
5101
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005102static Object* Runtime_StringCompare(Arguments args) {
5103 NoHandleAllocation ha;
5104 ASSERT(args.length() == 2);
5105
5106 CONVERT_CHECKED(String, x, args[0]);
5107 CONVERT_CHECKED(String, y, args[1]);
5108
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005109 Counters::string_compare_runtime.Increment();
5110
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005111 // A few fast case tests before we flatten.
5112 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005113 if (y->length() == 0) {
5114 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005115 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005116 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005117 return Smi::FromInt(LESS);
5118 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005119
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005120 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005121 if (d < 0) return Smi::FromInt(LESS);
5122 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005123
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005124 Object* obj = Heap::PrepareForCompare(x);
5125 if (obj->IsFailure()) return obj;
5126 obj = Heap::PrepareForCompare(y);
5127 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005128
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005129 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5130 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005131}
5132
5133
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005134static Object* Runtime_Math_acos(Arguments args) {
5135 NoHandleAllocation ha;
5136 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005137 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005138
5139 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005140 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005141}
5142
5143
5144static Object* Runtime_Math_asin(Arguments args) {
5145 NoHandleAllocation ha;
5146 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005147 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005148
5149 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005150 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005151}
5152
5153
5154static Object* Runtime_Math_atan(Arguments args) {
5155 NoHandleAllocation ha;
5156 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005157 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005158
5159 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005160 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005161}
5162
5163
5164static Object* Runtime_Math_atan2(Arguments args) {
5165 NoHandleAllocation ha;
5166 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005167 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005168
5169 CONVERT_DOUBLE_CHECKED(x, args[0]);
5170 CONVERT_DOUBLE_CHECKED(y, args[1]);
5171 double result;
5172 if (isinf(x) && isinf(y)) {
5173 // Make sure that the result in case of two infinite arguments
5174 // is a multiple of Pi / 4. The sign of the result is determined
5175 // by the first argument (x) and the sign of the second argument
5176 // determines the multiplier: one or three.
5177 static double kPiDividedBy4 = 0.78539816339744830962;
5178 int multiplier = (x < 0) ? -1 : 1;
5179 if (y < 0) multiplier *= 3;
5180 result = multiplier * kPiDividedBy4;
5181 } else {
5182 result = atan2(x, y);
5183 }
5184 return Heap::AllocateHeapNumber(result);
5185}
5186
5187
5188static Object* Runtime_Math_ceil(Arguments args) {
5189 NoHandleAllocation ha;
5190 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005191 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005192
5193 CONVERT_DOUBLE_CHECKED(x, args[0]);
5194 return Heap::NumberFromDouble(ceiling(x));
5195}
5196
5197
5198static Object* Runtime_Math_cos(Arguments args) {
5199 NoHandleAllocation ha;
5200 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005201 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005202
5203 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005204 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005205}
5206
5207
5208static Object* Runtime_Math_exp(Arguments args) {
5209 NoHandleAllocation ha;
5210 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005211 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005212
5213 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005214 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005215}
5216
5217
5218static Object* Runtime_Math_floor(Arguments args) {
5219 NoHandleAllocation ha;
5220 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005221 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005222
5223 CONVERT_DOUBLE_CHECKED(x, args[0]);
5224 return Heap::NumberFromDouble(floor(x));
5225}
5226
5227
5228static Object* Runtime_Math_log(Arguments args) {
5229 NoHandleAllocation ha;
5230 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005231 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005232
5233 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005234 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005235}
5236
5237
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005238// Helper function to compute x^y, where y is known to be an
5239// integer. Uses binary decomposition to limit the number of
5240// multiplications; see the discussion in "Hacker's Delight" by Henry
5241// S. Warren, Jr., figure 11-6, page 213.
5242static double powi(double x, int y) {
5243 ASSERT(y != kMinInt);
5244 unsigned n = (y < 0) ? -y : y;
5245 double m = x;
5246 double p = 1;
5247 while (true) {
5248 if ((n & 1) != 0) p *= m;
5249 n >>= 1;
5250 if (n == 0) {
5251 if (y < 0) {
5252 // Unfortunately, we have to be careful when p has reached
5253 // infinity in the computation, because sometimes the higher
5254 // internal precision in the pow() implementation would have
5255 // given us a finite p. This happens very rarely.
5256 double result = 1.0 / p;
5257 return (result == 0 && isinf(p))
5258 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
5259 : result;
5260 } else {
5261 return p;
5262 }
5263 }
5264 m *= m;
5265 }
5266}
5267
5268
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005269static Object* Runtime_Math_pow(Arguments args) {
5270 NoHandleAllocation ha;
5271 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005272 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005273
5274 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005275
5276 // If the second argument is a smi, it is much faster to call the
5277 // custom powi() function than the generic pow().
5278 if (args[1]->IsSmi()) {
5279 int y = Smi::cast(args[1])->value();
5280 return Heap::AllocateHeapNumber(powi(x, y));
5281 }
5282
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005283 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005284
5285 if (!isinf(x)) {
5286 if (y == 0.5) {
5287 // It's not uncommon to use Math.pow(x, 0.5) to compute the
5288 // square root of a number. To speed up such computations, we
5289 // explictly check for this case and use the sqrt() function
5290 // which is faster than pow().
5291 return Heap::AllocateHeapNumber(sqrt(x));
5292 } else if (y == -0.5) {
5293 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
5294 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
5295 }
5296 }
5297
5298 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005299 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005300 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5301 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005302 } else {
5303 return Heap::AllocateHeapNumber(pow(x, y));
5304 }
5305}
5306
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005307// Fast version of Math.pow if we know that y is not an integer and
5308// y is not -0.5 or 0.5. Used as slowcase from codegen.
5309static Object* Runtime_Math_pow_cfunction(Arguments args) {
5310 NoHandleAllocation ha;
5311 ASSERT(args.length() == 2);
5312 CONVERT_DOUBLE_CHECKED(x, args[0]);
5313 CONVERT_DOUBLE_CHECKED(y, args[1]);
5314 if (y == 0) {
5315 return Smi::FromInt(1);
5316 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
5317 return Heap::nan_value();
5318 } else {
5319 return Heap::AllocateHeapNumber(pow(x, y));
5320 }
5321}
5322
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005323
5324static Object* Runtime_Math_round(Arguments args) {
5325 NoHandleAllocation ha;
5326 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005327 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005328
5329 CONVERT_DOUBLE_CHECKED(x, args[0]);
5330 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005331 double integer = ceil(x);
5332 if (integer - x > 0.5) { integer -= 1.0; }
5333 return Heap::NumberFromDouble(integer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005334}
5335
5336
5337static Object* Runtime_Math_sin(Arguments args) {
5338 NoHandleAllocation ha;
5339 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005340 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005341
5342 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005343 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005344}
5345
5346
5347static Object* Runtime_Math_sqrt(Arguments args) {
5348 NoHandleAllocation ha;
5349 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005350 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005351
5352 CONVERT_DOUBLE_CHECKED(x, args[0]);
5353 return Heap::AllocateHeapNumber(sqrt(x));
5354}
5355
5356
5357static Object* Runtime_Math_tan(Arguments args) {
5358 NoHandleAllocation ha;
5359 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005360 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005361
5362 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005363 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005364}
5365
5366
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00005367static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005368 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
5369 181, 212, 243, 273, 304, 334};
5370 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
5371 182, 213, 244, 274, 305, 335};
5372
5373 year += month / 12;
5374 month %= 12;
5375 if (month < 0) {
5376 year--;
5377 month += 12;
5378 }
5379
5380 ASSERT(month >= 0);
5381 ASSERT(month < 12);
5382
5383 // year_delta is an arbitrary number such that:
5384 // a) year_delta = -1 (mod 400)
5385 // b) year + year_delta > 0 for years in the range defined by
5386 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
5387 // Jan 1 1970. This is required so that we don't run into integer
5388 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00005389 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005390 // operations.
5391 static const int year_delta = 399999;
5392 static const int base_day = 365 * (1970 + year_delta) +
5393 (1970 + year_delta) / 4 -
5394 (1970 + year_delta) / 100 +
5395 (1970 + year_delta) / 400;
5396
5397 int year1 = year + year_delta;
5398 int day_from_year = 365 * year1 +
5399 year1 / 4 -
5400 year1 / 100 +
5401 year1 / 400 -
5402 base_day;
5403
5404 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00005405 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005406 }
5407
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00005408 return day_from_year + day_from_month_leap[month] + day - 1;
5409}
5410
5411
5412static Object* Runtime_DateMakeDay(Arguments args) {
5413 NoHandleAllocation ha;
5414 ASSERT(args.length() == 3);
5415
5416 CONVERT_SMI_CHECKED(year, args[0]);
5417 CONVERT_SMI_CHECKED(month, args[1]);
5418 CONVERT_SMI_CHECKED(date, args[2]);
5419
5420 return Smi::FromInt(MakeDay(year, month, date));
5421}
5422
5423
5424static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
5425static const int kDaysIn4Years = 4 * 365 + 1;
5426static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
5427static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
5428static const int kDays1970to2000 = 30 * 365 + 7;
5429static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
5430 kDays1970to2000;
5431static const int kYearsOffset = 400000;
5432
5433static const char kDayInYear[] = {
5434 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5435 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5436 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5437 22, 23, 24, 25, 26, 27, 28,
5438 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5439 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5440 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5441 22, 23, 24, 25, 26, 27, 28, 29, 30,
5442 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5443 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5444 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5445 22, 23, 24, 25, 26, 27, 28, 29, 30,
5446 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5447 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5448 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5449 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5450 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5451 22, 23, 24, 25, 26, 27, 28, 29, 30,
5452 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5453 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5454 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5455 22, 23, 24, 25, 26, 27, 28, 29, 30,
5456 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5457 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5458
5459 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5460 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5461 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5462 22, 23, 24, 25, 26, 27, 28,
5463 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5464 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5465 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5466 22, 23, 24, 25, 26, 27, 28, 29, 30,
5467 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5468 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5469 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5470 22, 23, 24, 25, 26, 27, 28, 29, 30,
5471 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5472 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5473 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5474 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5475 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5476 22, 23, 24, 25, 26, 27, 28, 29, 30,
5477 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5478 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5479 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5480 22, 23, 24, 25, 26, 27, 28, 29, 30,
5481 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5482 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5483
5484 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5485 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5486 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5487 22, 23, 24, 25, 26, 27, 28, 29,
5488 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5489 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5490 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5491 22, 23, 24, 25, 26, 27, 28, 29, 30,
5492 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5493 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5494 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5495 22, 23, 24, 25, 26, 27, 28, 29, 30,
5496 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5497 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5498 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5499 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5500 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5501 22, 23, 24, 25, 26, 27, 28, 29, 30,
5502 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5503 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5504 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5505 22, 23, 24, 25, 26, 27, 28, 29, 30,
5506 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5507 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5508
5509 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5510 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5511 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5512 22, 23, 24, 25, 26, 27, 28,
5513 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5514 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5515 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5516 22, 23, 24, 25, 26, 27, 28, 29, 30,
5517 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5518 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5519 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5520 22, 23, 24, 25, 26, 27, 28, 29, 30,
5521 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5522 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5523 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5524 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5525 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5526 22, 23, 24, 25, 26, 27, 28, 29, 30,
5527 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5528 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5529 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5530 22, 23, 24, 25, 26, 27, 28, 29, 30,
5531 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5532 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
5533
5534static const char kMonthInYear[] = {
5535 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,
5536 0, 0, 0, 0, 0, 0,
5537 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,
5538 1, 1, 1,
5539 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,
5540 2, 2, 2, 2, 2, 2,
5541 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,
5542 3, 3, 3, 3, 3,
5543 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,
5544 4, 4, 4, 4, 4, 4,
5545 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,
5546 5, 5, 5, 5, 5,
5547 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,
5548 6, 6, 6, 6, 6, 6,
5549 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,
5550 7, 7, 7, 7, 7, 7,
5551 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,
5552 8, 8, 8, 8, 8,
5553 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,
5554 9, 9, 9, 9, 9, 9,
5555 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5556 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5557 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
5558 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
5559
5560 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,
5561 0, 0, 0, 0, 0, 0,
5562 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,
5563 1, 1, 1,
5564 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,
5565 2, 2, 2, 2, 2, 2,
5566 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,
5567 3, 3, 3, 3, 3,
5568 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,
5569 4, 4, 4, 4, 4, 4,
5570 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,
5571 5, 5, 5, 5, 5,
5572 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,
5573 6, 6, 6, 6, 6, 6,
5574 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,
5575 7, 7, 7, 7, 7, 7,
5576 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,
5577 8, 8, 8, 8, 8,
5578 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,
5579 9, 9, 9, 9, 9, 9,
5580 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5581 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5582 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
5583 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
5584
5585 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,
5586 0, 0, 0, 0, 0, 0,
5587 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,
5588 1, 1, 1, 1,
5589 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,
5590 2, 2, 2, 2, 2, 2,
5591 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,
5592 3, 3, 3, 3, 3,
5593 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,
5594 4, 4, 4, 4, 4, 4,
5595 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,
5596 5, 5, 5, 5, 5,
5597 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,
5598 6, 6, 6, 6, 6, 6,
5599 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,
5600 7, 7, 7, 7, 7, 7,
5601 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,
5602 8, 8, 8, 8, 8,
5603 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,
5604 9, 9, 9, 9, 9, 9,
5605 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5606 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5607 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
5608 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
5609
5610 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,
5611 0, 0, 0, 0, 0, 0,
5612 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,
5613 1, 1, 1,
5614 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,
5615 2, 2, 2, 2, 2, 2,
5616 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,
5617 3, 3, 3, 3, 3,
5618 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,
5619 4, 4, 4, 4, 4, 4,
5620 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,
5621 5, 5, 5, 5, 5,
5622 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,
5623 6, 6, 6, 6, 6, 6,
5624 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,
5625 7, 7, 7, 7, 7, 7,
5626 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,
5627 8, 8, 8, 8, 8,
5628 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,
5629 9, 9, 9, 9, 9, 9,
5630 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5631 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
5632 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
5633 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
5634
5635
5636// This function works for dates from 1970 to 2099.
5637static inline void DateYMDFromTimeAfter1970(int date,
5638 int &year, int &month, int &day) {
5639#ifdef DEBUG
5640 int save_date = date; // Need this for ASSERT in the end.
5641#endif
5642
5643 year = 1970 + (4 * date + 2) / kDaysIn4Years;
5644 date %= kDaysIn4Years;
5645
5646 month = kMonthInYear[date];
5647 day = kDayInYear[date];
5648
5649 ASSERT(MakeDay(year, month, day) == save_date);
5650}
5651
5652
5653static inline void DateYMDFromTimeSlow(int date,
5654 int &year, int &month, int &day) {
5655#ifdef DEBUG
5656 int save_date = date; // Need this for ASSERT in the end.
5657#endif
5658
5659 date += kDaysOffset;
5660 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
5661 date %= kDaysIn400Years;
5662
5663 ASSERT(MakeDay(year, 0, 1) + date == save_date);
5664
5665 date--;
5666 int yd1 = date / kDaysIn100Years;
5667 date %= kDaysIn100Years;
5668 year += 100 * yd1;
5669
5670 date++;
5671 int yd2 = date / kDaysIn4Years;
5672 date %= kDaysIn4Years;
5673 year += 4 * yd2;
5674
5675 date--;
5676 int yd3 = date / 365;
5677 date %= 365;
5678 year += yd3;
5679
5680 bool is_leap = (!yd1 || yd2) && !yd3;
5681
5682 ASSERT(date >= -1);
5683 ASSERT(is_leap || date >= 0);
5684 ASSERT(date < 365 || is_leap && date < 366);
5685 ASSERT(is_leap == (year % 4 == 0 && (year % 100 || (year % 400 == 0))));
5686 ASSERT(is_leap || MakeDay(year, 0, 1) + date == save_date);
5687 ASSERT(!is_leap || MakeDay(year, 0, 1) + date + 1 == save_date);
5688
5689 if (is_leap) {
5690 day = kDayInYear[2*365 + 1 + date];
5691 month = kMonthInYear[2*365 + 1 + date];
5692 } else {
5693 day = kDayInYear[date];
5694 month = kMonthInYear[date];
5695 }
5696
5697 ASSERT(MakeDay(year, month, day) == save_date);
5698}
5699
5700
5701static inline void DateYMDFromTime(int date,
5702 int &year, int &month, int &day) {
5703 if (date >= 0 && date < 32 * kDaysIn4Years) {
5704 DateYMDFromTimeAfter1970(date, year, month, day);
5705 } else {
5706 DateYMDFromTimeSlow(date, year, month, day);
5707 }
5708}
5709
5710
5711static Object* Runtime_DateYMDFromTime(Arguments args) {
5712 NoHandleAllocation ha;
5713 ASSERT(args.length() == 2);
5714
5715 CONVERT_DOUBLE_CHECKED(t, args[0]);
5716 CONVERT_CHECKED(JSArray, res_array, args[1]);
5717
5718 int year, month, day;
5719 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
5720
5721 res_array->SetElement(0, Smi::FromInt(year));
5722 res_array->SetElement(1, Smi::FromInt(month));
5723 res_array->SetElement(2, Smi::FromInt(day));
5724
5725 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005726}
5727
5728
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005729static Object* Runtime_NewArgumentsFast(Arguments args) {
5730 NoHandleAllocation ha;
5731 ASSERT(args.length() == 3);
5732
5733 JSFunction* callee = JSFunction::cast(args[0]);
5734 Object** parameters = reinterpret_cast<Object**>(args[1]);
5735 const int length = Smi::cast(args[2])->value();
5736
5737 Object* result = Heap::AllocateArgumentsObject(callee, length);
5738 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005739 // Allocate the elements if needed.
5740 if (length > 0) {
5741 // Allocate the fixed array.
5742 Object* obj = Heap::AllocateRawFixedArray(length);
5743 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005744
5745 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005746 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
5747 FixedArray* array = FixedArray::cast(obj);
5748 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005749
5750 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005751 for (int i = 0; i < length; i++) {
5752 array->set(i, *--parameters, mode);
5753 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005754 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00005755 }
5756 return result;
5757}
5758
5759
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005760static Object* Runtime_NewClosure(Arguments args) {
5761 HandleScope scope;
5762 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00005763 CONVERT_ARG_CHECKED(Context, context, 0);
5764 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005765
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005766 PretenureFlag pretenure = (context->global_context() == *context)
5767 ? TENURED // Allocate global closures in old space.
5768 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005769 Handle<JSFunction> result =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00005770 Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005771 return *result;
5772}
5773
5774
ager@chromium.org5c838252010-02-19 08:53:10 +00005775static Code* ComputeConstructStub(Handle<JSFunction> function) {
5776 Handle<Object> prototype = Factory::null_value();
5777 if (function->has_instance_prototype()) {
5778 prototype = Handle<Object>(function->instance_prototype());
5779 }
5780 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005781 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00005782 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005783 if (code->IsFailure()) {
5784 return Builtins::builtin(Builtins::JSConstructStubGeneric);
5785 }
5786 return Code::cast(code);
5787 }
5788
ager@chromium.org5c838252010-02-19 08:53:10 +00005789 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005790}
5791
5792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005794 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005795 ASSERT(args.length() == 1);
5796
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005797 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005798
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005799 // If the constructor isn't a proper function we throw a type error.
5800 if (!constructor->IsJSFunction()) {
5801 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
5802 Handle<Object> type_error =
5803 Factory::NewTypeError("not_constructor", arguments);
5804 return Top::Throw(*type_error);
5805 }
5806
5807 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005808#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005809 // Handle stepping into constructors if step into is active.
5810 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00005811 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005812 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005813#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005814
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005815 if (function->has_initial_map()) {
5816 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005817 // The 'Function' function ignores the receiver object when
5818 // called using 'new' and creates a new JSFunction object that
5819 // is returned. The receiver object is only used for error
5820 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005821 // JSFunction. Factory::NewJSObject() should not be used to
5822 // allocate JSFunctions since it does not properly initialize
5823 // the shared part of the function. Since the receiver is
5824 // ignored anyway, we use the global object as the receiver
5825 // instead of a new JSFunction object. This way, errors are
5826 // reported the same way whether or not 'Function' is called
5827 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005828 return Top::context()->global();
5829 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005830 }
5831
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005832 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005833 Handle<SharedFunctionInfo> shared(function->shared());
5834 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005835
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005836 bool first_allocation = !function->has_initial_map();
5837 Handle<JSObject> result = Factory::NewJSObject(function);
5838 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005839 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00005840 ComputeConstructStub(Handle<JSFunction>(function)));
5841 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005842 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005843
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00005844 Counters::constructed_objects.Increment();
5845 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005846
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005847 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005848}
5849
5850
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005851static Object* Runtime_LazyCompile(Arguments args) {
5852 HandleScope scope;
5853 ASSERT(args.length() == 1);
5854
5855 Handle<JSFunction> function = args.at<JSFunction>(0);
5856#ifdef DEBUG
5857 if (FLAG_trace_lazy) {
5858 PrintF("[lazy: ");
5859 function->shared()->name()->Print();
5860 PrintF("]\n");
5861 }
5862#endif
5863
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005864 // Compile the target function. Here we compile using CompileLazyInLoop in
5865 // order to get the optimized version. This helps code like delta-blue
5866 // that calls performance-critical routines through constructors. A
5867 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
5868 // direct call. Since the in-loop tracking takes place through CallICs
5869 // this means that things called through constructors are never known to
5870 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005871 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005872 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005873 return Failure::Exception();
5874 }
5875
5876 return function->code();
5877}
5878
5879
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005880static Object* Runtime_GetFunctionDelegate(Arguments args) {
5881 HandleScope scope;
5882 ASSERT(args.length() == 1);
5883 RUNTIME_ASSERT(!args[0]->IsJSFunction());
5884 return *Execution::GetFunctionDelegate(args.at<Object>(0));
5885}
5886
5887
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00005888static Object* Runtime_GetConstructorDelegate(Arguments args) {
5889 HandleScope scope;
5890 ASSERT(args.length() == 1);
5891 RUNTIME_ASSERT(!args[0]->IsJSFunction());
5892 return *Execution::GetConstructorDelegate(args.at<Object>(0));
5893}
5894
5895
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005896static Object* Runtime_NewContext(Arguments args) {
5897 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00005898 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005899
kasper.lund7276f142008-07-30 08:49:36 +00005900 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005901 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
5902 Object* result = Heap::AllocateFunctionContext(length, function);
5903 if (result->IsFailure()) return result;
5904
5905 Top::set_context(Context::cast(result));
5906
kasper.lund7276f142008-07-30 08:49:36 +00005907 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005908}
5909
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005910static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005911 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005912 Object* js_object = object;
5913 if (!js_object->IsJSObject()) {
5914 js_object = js_object->ToObject();
5915 if (js_object->IsFailure()) {
5916 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005917 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005918 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005919 Handle<Object> result =
5920 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
5921 return Top::Throw(*result);
5922 }
5923 }
5924
5925 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005926 Heap::AllocateWithContext(Top::context(),
5927 JSObject::cast(js_object),
5928 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005929 if (result->IsFailure()) return result;
5930
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005931 Context* context = Context::cast(result);
5932 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005933
kasper.lund7276f142008-07-30 08:49:36 +00005934 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005935}
5936
5937
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005938static Object* Runtime_PushContext(Arguments args) {
5939 NoHandleAllocation ha;
5940 ASSERT(args.length() == 1);
5941 return PushContextHelper(args[0], false);
5942}
5943
5944
5945static Object* Runtime_PushCatchContext(Arguments args) {
5946 NoHandleAllocation ha;
5947 ASSERT(args.length() == 1);
5948 return PushContextHelper(args[0], true);
5949}
5950
5951
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005952static Object* Runtime_LookupContext(Arguments args) {
5953 HandleScope scope;
5954 ASSERT(args.length() == 2);
5955
5956 CONVERT_ARG_CHECKED(Context, context, 0);
5957 CONVERT_ARG_CHECKED(String, name, 1);
5958
5959 int index;
5960 PropertyAttributes attributes;
5961 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005962 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005963 context->Lookup(name, flags, &index, &attributes);
5964
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005965 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005966 ASSERT(holder->IsJSObject());
5967 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005968 }
5969
5970 // No intermediate context found. Use global object by default.
5971 return Top::context()->global();
5972}
5973
5974
ager@chromium.orga1645e22009-09-09 19:27:10 +00005975// A mechanism to return a pair of Object pointers in registers (if possible).
5976// How this is achieved is calling convention-dependent.
5977// All currently supported x86 compiles uses calling conventions that are cdecl
5978// variants where a 64-bit value is returned in two 32-bit registers
5979// (edx:eax on ia32, r1:r0 on ARM).
5980// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
5981// In Win64 calling convention, a struct of two pointers is returned in memory,
5982// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005983#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005984struct ObjectPair {
5985 Object* x;
5986 Object* y;
5987};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005988
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005989static inline ObjectPair MakePair(Object* x, Object* y) {
5990 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00005991 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
5992 // In Win64 they are assigned to a hidden first argument.
5993 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005994}
5995#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005996typedef uint64_t ObjectPair;
5997static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005998 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005999 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006000}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006001#endif
6002
6003
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006004static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006005 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6006 USE(attributes);
6007 return x->IsTheHole() ? Heap::undefined_value() : x;
6008}
6009
6010
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006011static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6012 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006013 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006014 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006015 JSFunction* context_extension_function =
6016 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006017 // If the holder isn't a context extension object, we just return it
6018 // as the receiver. This allows arguments objects to be used as
6019 // receivers, but only if they are put in the context scope chain
6020 // explicitly via a with-statement.
6021 Object* constructor = holder->map()->constructor();
6022 if (constructor != context_extension_function) return holder;
6023 // Fall back to using the global object as the receiver if the
6024 // property turns out to be a local variable allocated in a context
6025 // extension object - introduced via eval.
6026 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006027}
6028
6029
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006030static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006031 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006032 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006033
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006034 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006035 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006036 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006037 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006038 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006039
6040 int index;
6041 PropertyAttributes attributes;
6042 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006043 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006044 context->Lookup(name, flags, &index, &attributes);
6045
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006046 // If the index is non-negative, the slot has been found in a local
6047 // variable or a parameter. Read it from the context object or the
6048 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006049 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006050 // If the "property" we were looking for is a local variable or an
6051 // argument in a context, the receiver is the global object; see
6052 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6053 JSObject* receiver = Top::context()->global()->global_receiver();
6054 Object* value = (holder->IsContext())
6055 ? Context::cast(*holder)->get(index)
6056 : JSObject::cast(*holder)->GetElement(index);
6057 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006058 }
6059
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006060 // If the holder is found, we read the property from it.
6061 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006062 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006063 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006064 JSObject* receiver;
6065 if (object->IsGlobalObject()) {
6066 receiver = GlobalObject::cast(object)->global_receiver();
6067 } else if (context->is_exception_holder(*holder)) {
6068 receiver = Top::context()->global()->global_receiver();
6069 } else {
6070 receiver = ComputeReceiverForNonGlobal(object);
6071 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006072 // No need to unhole the value here. This is taken care of by the
6073 // GetProperty function.
6074 Object* value = object->GetProperty(*name);
6075 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006076 }
6077
6078 if (throw_error) {
6079 // The property doesn't exist - throw exception.
6080 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006081 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006082 return MakePair(Top::Throw(*reference_error), NULL);
6083 } else {
6084 // The property doesn't exist - return undefined
6085 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6086 }
6087}
6088
6089
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006090static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006091 return LoadContextSlotHelper(args, true);
6092}
6093
6094
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006095static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006096 return LoadContextSlotHelper(args, false);
6097}
6098
6099
6100static Object* Runtime_StoreContextSlot(Arguments args) {
6101 HandleScope scope;
6102 ASSERT(args.length() == 3);
6103
6104 Handle<Object> value(args[0]);
6105 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006106 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006107
6108 int index;
6109 PropertyAttributes attributes;
6110 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006111 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006112 context->Lookup(name, flags, &index, &attributes);
6113
6114 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006115 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006116 // Ignore if read_only variable.
6117 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006118 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006119 }
6120 } else {
6121 ASSERT((attributes & READ_ONLY) == 0);
6122 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006123 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006124 USE(result);
6125 ASSERT(!result->IsFailure());
6126 }
6127 return *value;
6128 }
6129
6130 // Slow case: The property is not in a FixedArray context.
6131 // It is either in an JSObject extension context or it was not found.
6132 Handle<JSObject> context_ext;
6133
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006134 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006135 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006136 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006137 } else {
6138 // The property was not found. It needs to be stored in the global context.
6139 ASSERT(attributes == ABSENT);
6140 attributes = NONE;
6141 context_ext = Handle<JSObject>(Top::context()->global());
6142 }
6143
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006144 // Set the property, but ignore if read_only variable on the context
6145 // extension object itself.
6146 if ((attributes & READ_ONLY) == 0 ||
6147 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006148 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6149 if (set.is_null()) {
6150 // Failure::Exception is converted to a null handle in the
6151 // handle-based methods such as SetProperty. We therefore need
6152 // to convert null handles back to exceptions.
6153 ASSERT(Top::has_pending_exception());
6154 return Failure::Exception();
6155 }
6156 }
6157 return *value;
6158}
6159
6160
6161static Object* Runtime_Throw(Arguments args) {
6162 HandleScope scope;
6163 ASSERT(args.length() == 1);
6164
6165 return Top::Throw(args[0]);
6166}
6167
6168
6169static Object* Runtime_ReThrow(Arguments args) {
6170 HandleScope scope;
6171 ASSERT(args.length() == 1);
6172
6173 return Top::ReThrow(args[0]);
6174}
6175
6176
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006177static Object* Runtime_PromoteScheduledException(Arguments args) {
6178 ASSERT_EQ(0, args.length());
6179 return Top::PromoteScheduledException();
6180}
6181
6182
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006183static Object* Runtime_ThrowReferenceError(Arguments args) {
6184 HandleScope scope;
6185 ASSERT(args.length() == 1);
6186
6187 Handle<Object> name(args[0]);
6188 Handle<Object> reference_error =
6189 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6190 return Top::Throw(*reference_error);
6191}
6192
6193
6194static Object* Runtime_StackOverflow(Arguments args) {
6195 NoHandleAllocation na;
6196 return Top::StackOverflow();
6197}
6198
6199
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006200static Object* Runtime_StackGuard(Arguments args) {
6201 ASSERT(args.length() == 1);
6202
6203 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006204 if (StackGuard::IsStackOverflow()) {
6205 return Runtime_StackOverflow(args);
6206 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006207
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006208 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006209}
6210
6211
6212// NOTE: These PrintXXX functions are defined for all builds (not just
6213// DEBUG builds) because we may want to be able to trace function
6214// calls in all modes.
6215static void PrintString(String* str) {
6216 // not uncommon to have empty strings
6217 if (str->length() > 0) {
6218 SmartPointer<char> s =
6219 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
6220 PrintF("%s", *s);
6221 }
6222}
6223
6224
6225static void PrintObject(Object* obj) {
6226 if (obj->IsSmi()) {
6227 PrintF("%d", Smi::cast(obj)->value());
6228 } else if (obj->IsString() || obj->IsSymbol()) {
6229 PrintString(String::cast(obj));
6230 } else if (obj->IsNumber()) {
6231 PrintF("%g", obj->Number());
6232 } else if (obj->IsFailure()) {
6233 PrintF("<failure>");
6234 } else if (obj->IsUndefined()) {
6235 PrintF("<undefined>");
6236 } else if (obj->IsNull()) {
6237 PrintF("<null>");
6238 } else if (obj->IsTrue()) {
6239 PrintF("<true>");
6240 } else if (obj->IsFalse()) {
6241 PrintF("<false>");
6242 } else {
6243 PrintF("%p", obj);
6244 }
6245}
6246
6247
6248static int StackSize() {
6249 int n = 0;
6250 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
6251 return n;
6252}
6253
6254
6255static void PrintTransition(Object* result) {
6256 // indentation
6257 { const int nmax = 80;
6258 int n = StackSize();
6259 if (n <= nmax)
6260 PrintF("%4d:%*s", n, n, "");
6261 else
6262 PrintF("%4d:%*s", n, nmax, "...");
6263 }
6264
6265 if (result == NULL) {
6266 // constructor calls
6267 JavaScriptFrameIterator it;
6268 JavaScriptFrame* frame = it.frame();
6269 if (frame->IsConstructor()) PrintF("new ");
6270 // function name
6271 Object* fun = frame->function();
6272 if (fun->IsJSFunction()) {
6273 PrintObject(JSFunction::cast(fun)->shared()->name());
6274 } else {
6275 PrintObject(fun);
6276 }
6277 // function arguments
6278 // (we are intentionally only printing the actually
6279 // supplied parameters, not all parameters required)
6280 PrintF("(this=");
6281 PrintObject(frame->receiver());
6282 const int length = frame->GetProvidedParametersCount();
6283 for (int i = 0; i < length; i++) {
6284 PrintF(", ");
6285 PrintObject(frame->GetParameter(i));
6286 }
6287 PrintF(") {\n");
6288
6289 } else {
6290 // function result
6291 PrintF("} -> ");
6292 PrintObject(result);
6293 PrintF("\n");
6294 }
6295}
6296
6297
6298static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006299 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006300 NoHandleAllocation ha;
6301 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006302 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006303}
6304
6305
6306static Object* Runtime_TraceExit(Arguments args) {
6307 NoHandleAllocation ha;
6308 PrintTransition(args[0]);
6309 return args[0]; // return TOS
6310}
6311
6312
6313static Object* Runtime_DebugPrint(Arguments args) {
6314 NoHandleAllocation ha;
6315 ASSERT(args.length() == 1);
6316
6317#ifdef DEBUG
6318 if (args[0]->IsString()) {
6319 // If we have a string, assume it's a code "marker"
6320 // and print some interesting cpu debugging info.
6321 JavaScriptFrameIterator it;
6322 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006323 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
6324 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006325 } else {
6326 PrintF("DebugPrint: ");
6327 }
6328 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006329 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006330 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00006331 HeapObject::cast(args[0])->map()->Print();
6332 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006333#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006334 // ShortPrint is available in release mode. Print is not.
6335 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006336#endif
6337 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00006338 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006339
6340 return args[0]; // return TOS
6341}
6342
6343
6344static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006345 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006346 NoHandleAllocation ha;
6347 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006348 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006349}
6350
6351
mads.s.ager31e71382008-08-13 09:32:07 +00006352static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006353 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00006354 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006355
6356 // According to ECMA-262, section 15.9.1, page 117, the precision of
6357 // the number in a Date object representing a particular instant in
6358 // time is milliseconds. Therefore, we floor the result of getting
6359 // the OS time.
6360 double millis = floor(OS::TimeCurrentMillis());
6361 return Heap::NumberFromDouble(millis);
6362}
6363
6364
6365static Object* Runtime_DateParseString(Arguments args) {
6366 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006367 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006368
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006369 CONVERT_ARG_CHECKED(String, str, 0);
6370 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006371
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006372 CONVERT_ARG_CHECKED(JSArray, output, 1);
6373 RUNTIME_ASSERT(output->HasFastElements());
6374
6375 AssertNoAllocation no_allocation;
6376
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006377 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006378 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
6379 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00006380 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006381 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006382 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00006383 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006384 result = DateParser::Parse(str->ToUC16Vector(), output_array);
6385 }
6386
6387 if (result) {
6388 return *output;
6389 } else {
6390 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006391 }
6392}
6393
6394
6395static Object* Runtime_DateLocalTimezone(Arguments args) {
6396 NoHandleAllocation ha;
6397 ASSERT(args.length() == 1);
6398
6399 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00006400 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006401 return Heap::AllocateStringFromUtf8(CStrVector(zone));
6402}
6403
6404
6405static Object* Runtime_DateLocalTimeOffset(Arguments args) {
6406 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00006407 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006408
6409 return Heap::NumberFromDouble(OS::LocalTimeOffset());
6410}
6411
6412
6413static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
6414 NoHandleAllocation ha;
6415 ASSERT(args.length() == 1);
6416
6417 CONVERT_DOUBLE_CHECKED(x, args[0]);
6418 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
6419}
6420
6421
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006422static Object* Runtime_NumberIsFinite(Arguments args) {
6423 NoHandleAllocation ha;
6424 ASSERT(args.length() == 1);
6425
6426 CONVERT_DOUBLE_CHECKED(value, args[0]);
6427 Object* result;
6428 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
6429 result = Heap::false_value();
6430 } else {
6431 result = Heap::true_value();
6432 }
6433 return result;
6434}
6435
6436
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006437static Object* Runtime_GlobalReceiver(Arguments args) {
6438 ASSERT(args.length() == 1);
6439 Object* global = args[0];
6440 if (!global->IsJSGlobalObject()) return Heap::null_value();
6441 return JSGlobalObject::cast(global)->global_receiver();
6442}
6443
6444
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006445static Object* Runtime_CompileString(Arguments args) {
6446 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006447 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00006448 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006449 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006450
ager@chromium.org381abbb2009-02-25 13:23:22 +00006451 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006452 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006453 Compiler::ValidationState validate = (is_json->IsTrue())
6454 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006455 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
6456 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006457 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00006458 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00006459 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006460 Handle<JSFunction> fun =
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006461 Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006462 return *fun;
6463}
6464
6465
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006466static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
6467 ASSERT(args.length() == 3);
6468 if (!args[0]->IsJSFunction()) {
6469 return MakePair(Top::ThrowIllegalOperation(), NULL);
6470 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006471
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006472 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006473 Handle<JSFunction> callee = args.at<JSFunction>(0);
6474 Handle<Object> receiver; // Will be overwritten.
6475
6476 // Compute the calling context.
6477 Handle<Context> context = Handle<Context>(Top::context());
6478#ifdef DEBUG
6479 // Make sure Top::context() agrees with the old code that traversed
6480 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006481 StackFrameLocator locator;
6482 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006483 ASSERT(Context::cast(frame->context()) == *context);
6484#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006485
6486 // Find where the 'eval' symbol is bound. It is unaliased only if
6487 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006488 int index = -1;
6489 PropertyAttributes attributes = ABSENT;
6490 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006491 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
6492 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006493 // Stop search when eval is found or when the global context is
6494 // reached.
6495 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006496 if (context->is_function_context()) {
6497 context = Handle<Context>(Context::cast(context->closure()->context()));
6498 } else {
6499 context = Handle<Context>(context->previous());
6500 }
6501 }
6502
iposva@chromium.org245aa852009-02-10 00:49:54 +00006503 // If eval could not be resolved, it has been deleted and we need to
6504 // throw a reference error.
6505 if (attributes == ABSENT) {
6506 Handle<Object> name = Factory::eval_symbol();
6507 Handle<Object> reference_error =
6508 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006509 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006510 }
6511
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006512 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006513 // 'eval' is not bound in the global context. Just call the function
6514 // with the given arguments. This is not necessarily the global eval.
6515 if (receiver->IsContext()) {
6516 context = Handle<Context>::cast(receiver);
6517 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006518 } else if (receiver->IsJSContextExtensionObject()) {
6519 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006520 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006521 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006522 }
6523
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006524 // 'eval' is bound in the global context, but it may have been overwritten.
6525 // Compare it to the builtin 'GlobalEval' function to make sure.
6526 if (*callee != Top::global_context()->global_eval_fun() ||
6527 !args[1]->IsString()) {
6528 return MakePair(*callee, Top::context()->global()->global_receiver());
6529 }
6530
6531 // Deal with a normal eval call with a string argument. Compile it
6532 // and return the compiled function bound in the local context.
6533 Handle<String> source = args.at<String>(1);
6534 Handle<JSFunction> boilerplate = Compiler::CompileEval(
6535 source,
6536 Handle<Context>(Top::context()),
6537 Top::context()->IsGlobalContext(),
6538 Compiler::DONT_VALIDATE_JSON);
6539 if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
6540 callee = Factory::NewFunctionFromBoilerplate(
6541 boilerplate,
6542 Handle<Context>(Top::context()),
6543 NOT_TENURED);
6544 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006545}
6546
6547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006548static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
6549 // This utility adjusts the property attributes for newly created Function
6550 // object ("new Function(...)") by changing the map.
6551 // All it does is changing the prototype property to enumerable
6552 // as specified in ECMA262, 15.3.5.2.
6553 HandleScope scope;
6554 ASSERT(args.length() == 1);
6555 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6556 ASSERT(func->map()->instance_type() ==
6557 Top::function_instance_map()->instance_type());
6558 ASSERT(func->map()->instance_size() ==
6559 Top::function_instance_map()->instance_size());
6560 func->set_map(*Top::function_instance_map());
6561 return *func;
6562}
6563
6564
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006565// Push an array unto an array of arrays if it is not already in the
6566// array. Returns true if the element was pushed on the stack and
6567// false otherwise.
6568static Object* Runtime_PushIfAbsent(Arguments args) {
6569 ASSERT(args.length() == 2);
6570 CONVERT_CHECKED(JSArray, array, args[0]);
6571 CONVERT_CHECKED(JSArray, element, args[1]);
6572 RUNTIME_ASSERT(array->HasFastElements());
6573 int length = Smi::cast(array->length())->value();
6574 FixedArray* elements = FixedArray::cast(array->elements());
6575 for (int i = 0; i < length; i++) {
6576 if (elements->get(i) == element) return Heap::false_value();
6577 }
6578 Object* obj = array->SetFastElement(length, element);
6579 if (obj->IsFailure()) return obj;
6580 return Heap::true_value();
6581}
6582
6583
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006584/**
6585 * A simple visitor visits every element of Array's.
6586 * The backend storage can be a fixed array for fast elements case,
6587 * or a dictionary for sparse array. Since Dictionary is a subtype
6588 * of FixedArray, the class can be used by both fast and slow cases.
6589 * The second parameter of the constructor, fast_elements, specifies
6590 * whether the storage is a FixedArray or Dictionary.
6591 *
6592 * An index limit is used to deal with the situation that a result array
6593 * length overflows 32-bit non-negative integer.
6594 */
6595class ArrayConcatVisitor {
6596 public:
6597 ArrayConcatVisitor(Handle<FixedArray> storage,
6598 uint32_t index_limit,
6599 bool fast_elements) :
6600 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006601 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006602
6603 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006604 if (i >= index_limit_ - index_offset_) return;
6605 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006606
6607 if (fast_elements_) {
6608 ASSERT(index < static_cast<uint32_t>(storage_->length()));
6609 storage_->set(index, *elm);
6610
6611 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006612 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
6613 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006614 Factory::DictionaryAtNumberPut(dict, index, elm);
6615 if (!result.is_identical_to(dict))
6616 storage_ = result;
6617 }
6618 }
6619
6620 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006621 if (index_limit_ - index_offset_ < delta) {
6622 index_offset_ = index_limit_;
6623 } else {
6624 index_offset_ += delta;
6625 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006626 }
6627
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00006628 Handle<FixedArray> storage() { return storage_; }
6629
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006630 private:
6631 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006632 // Limit on the accepted indices. Elements with indices larger than the
6633 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006634 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006635 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006636 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006637 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006638};
6639
6640
ager@chromium.org3811b432009-10-28 14:53:37 +00006641template<class ExternalArrayClass, class ElementType>
6642static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
6643 bool elements_are_ints,
6644 bool elements_are_guaranteed_smis,
6645 uint32_t range,
6646 ArrayConcatVisitor* visitor) {
6647 Handle<ExternalArrayClass> array(
6648 ExternalArrayClass::cast(receiver->elements()));
6649 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
6650
6651 if (visitor != NULL) {
6652 if (elements_are_ints) {
6653 if (elements_are_guaranteed_smis) {
6654 for (uint32_t j = 0; j < len; j++) {
6655 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
6656 visitor->visit(j, e);
6657 }
6658 } else {
6659 for (uint32_t j = 0; j < len; j++) {
6660 int64_t val = static_cast<int64_t>(array->get(j));
6661 if (Smi::IsValid(static_cast<intptr_t>(val))) {
6662 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
6663 visitor->visit(j, e);
6664 } else {
6665 Handle<Object> e(
6666 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
6667 visitor->visit(j, e);
6668 }
6669 }
6670 }
6671 } else {
6672 for (uint32_t j = 0; j < len; j++) {
6673 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
6674 visitor->visit(j, e);
6675 }
6676 }
6677 }
6678
6679 return len;
6680}
6681
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006682/**
6683 * A helper function that visits elements of a JSObject. Only elements
6684 * whose index between 0 and range (exclusive) are visited.
6685 *
6686 * If the third parameter, visitor, is not NULL, the visitor is called
6687 * with parameters, 'visitor_index_offset + element index' and the element.
6688 *
6689 * It returns the number of visisted elements.
6690 */
6691static uint32_t IterateElements(Handle<JSObject> receiver,
6692 uint32_t range,
6693 ArrayConcatVisitor* visitor) {
6694 uint32_t num_of_elements = 0;
6695
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006696 switch (receiver->GetElementsKind()) {
6697 case JSObject::FAST_ELEMENTS: {
6698 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
6699 uint32_t len = elements->length();
6700 if (range < len) {
6701 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006702 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006703
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006704 for (uint32_t j = 0; j < len; j++) {
6705 Handle<Object> e(elements->get(j));
6706 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006707 num_of_elements++;
6708 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006709 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006710 }
6711 }
6712 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006713 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006714 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006715 case JSObject::PIXEL_ELEMENTS: {
6716 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
6717 uint32_t len = pixels->length();
6718 if (range < len) {
6719 len = range;
6720 }
6721
6722 for (uint32_t j = 0; j < len; j++) {
6723 num_of_elements++;
6724 if (visitor != NULL) {
6725 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
6726 visitor->visit(j, e);
6727 }
6728 }
6729 break;
6730 }
ager@chromium.org3811b432009-10-28 14:53:37 +00006731 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
6732 num_of_elements =
6733 IterateExternalArrayElements<ExternalByteArray, int8_t>(
6734 receiver, true, true, range, visitor);
6735 break;
6736 }
6737 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
6738 num_of_elements =
6739 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
6740 receiver, true, true, range, visitor);
6741 break;
6742 }
6743 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
6744 num_of_elements =
6745 IterateExternalArrayElements<ExternalShortArray, int16_t>(
6746 receiver, true, true, range, visitor);
6747 break;
6748 }
6749 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
6750 num_of_elements =
6751 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
6752 receiver, true, true, range, visitor);
6753 break;
6754 }
6755 case JSObject::EXTERNAL_INT_ELEMENTS: {
6756 num_of_elements =
6757 IterateExternalArrayElements<ExternalIntArray, int32_t>(
6758 receiver, true, false, range, visitor);
6759 break;
6760 }
6761 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
6762 num_of_elements =
6763 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
6764 receiver, true, false, range, visitor);
6765 break;
6766 }
6767 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
6768 num_of_elements =
6769 IterateExternalArrayElements<ExternalFloatArray, float>(
6770 receiver, false, false, range, visitor);
6771 break;
6772 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00006773 case JSObject::DICTIONARY_ELEMENTS: {
6774 Handle<NumberDictionary> dict(receiver->element_dictionary());
6775 uint32_t capacity = dict->Capacity();
6776 for (uint32_t j = 0; j < capacity; j++) {
6777 Handle<Object> k(dict->KeyAt(j));
6778 if (dict->IsKey(*k)) {
6779 ASSERT(k->IsNumber());
6780 uint32_t index = static_cast<uint32_t>(k->Number());
6781 if (index < range) {
6782 num_of_elements++;
6783 if (visitor) {
6784 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
6785 }
6786 }
6787 }
6788 }
6789 break;
6790 }
6791 default:
6792 UNREACHABLE();
6793 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006794 }
6795
6796 return num_of_elements;
6797}
6798
6799
6800/**
6801 * A helper function that visits elements of an Array object, and elements
6802 * on its prototypes.
6803 *
6804 * Elements on prototypes are visited first, and only elements whose indices
6805 * less than Array length are visited.
6806 *
6807 * If a ArrayConcatVisitor object is given, the visitor is called with
6808 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006809 *
6810 * The returned number of elements is an upper bound on the actual number
6811 * of elements added. If the same element occurs in more than one object
6812 * in the array's prototype chain, it will be counted more than once, but
6813 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006814 */
6815static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
6816 ArrayConcatVisitor* visitor) {
6817 uint32_t range = static_cast<uint32_t>(array->length()->Number());
6818 Handle<Object> obj = array;
6819
6820 static const int kEstimatedPrototypes = 3;
6821 List< Handle<JSObject> > objects(kEstimatedPrototypes);
6822
6823 // Visit prototype first. If an element on the prototype is shadowed by
6824 // the inheritor using the same index, the ArrayConcatVisitor visits
6825 // the prototype element before the shadowing element.
6826 // The visitor can simply overwrite the old value by new value using
6827 // the same index. This follows Array::concat semantics.
6828 while (!obj->IsNull()) {
6829 objects.Add(Handle<JSObject>::cast(obj));
6830 obj = Handle<Object>(obj->GetPrototype());
6831 }
6832
6833 uint32_t nof_elements = 0;
6834 for (int i = objects.length() - 1; i >= 0; i--) {
6835 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006836 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006837 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006838
6839 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
6840 nof_elements = JSObject::kMaxElementCount;
6841 } else {
6842 nof_elements += encountered_elements;
6843 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006844 }
6845
6846 return nof_elements;
6847}
6848
6849
6850/**
6851 * A helper function of Runtime_ArrayConcat.
6852 *
6853 * The first argument is an Array of arrays and objects. It is the
6854 * same as the arguments array of Array::concat JS function.
6855 *
6856 * If an argument is an Array object, the function visits array
6857 * elements. If an argument is not an Array object, the function
6858 * visits the object as if it is an one-element array.
6859 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006860 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006861 * non-negative number is used as new length. For example, if one
6862 * array length is 2^32 - 1, second array length is 1, the
6863 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006864 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
6865 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006866 */
6867static uint32_t IterateArguments(Handle<JSArray> arguments,
6868 ArrayConcatVisitor* visitor) {
6869 uint32_t visited_elements = 0;
6870 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
6871
6872 for (uint32_t i = 0; i < num_of_args; i++) {
6873 Handle<Object> obj(arguments->GetElement(i));
6874 if (obj->IsJSArray()) {
6875 Handle<JSArray> array = Handle<JSArray>::cast(obj);
6876 uint32_t len = static_cast<uint32_t>(array->length()->Number());
6877 uint32_t nof_elements =
6878 IterateArrayAndPrototypeElements(array, visitor);
6879 // Total elements of array and its prototype chain can be more than
6880 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006881 // the array length number of elements. We use the length as an estimate
6882 // for the actual number of elements added.
6883 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
6884 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
6885 visited_elements = JSArray::kMaxElementCount;
6886 } else {
6887 visited_elements += added_elements;
6888 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006889 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006890 } else {
6891 if (visitor) {
6892 visitor->visit(0, obj);
6893 visitor->increase_index_offset(1);
6894 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006895 if (visited_elements < JSArray::kMaxElementCount) {
6896 visited_elements++;
6897 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006898 }
6899 }
6900 return visited_elements;
6901}
6902
6903
6904/**
6905 * Array::concat implementation.
6906 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006907 * TODO(lrn): Fix non-compliance for very large concatenations and update to
6908 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006909 */
6910static Object* Runtime_ArrayConcat(Arguments args) {
6911 ASSERT(args.length() == 1);
6912 HandleScope handle_scope;
6913
6914 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
6915 Handle<JSArray> arguments(arg_arrays);
6916
6917 // Pass 1: estimate the number of elements of the result
6918 // (it could be more than real numbers if prototype has elements).
6919 uint32_t result_length = 0;
6920 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
6921
6922 { AssertNoAllocation nogc;
6923 for (uint32_t i = 0; i < num_of_args; i++) {
6924 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006925 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006926 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006927 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006928 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
6929 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006930 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006931 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006932 if (JSObject::kMaxElementCount - result_length < length_estimate) {
6933 result_length = JSObject::kMaxElementCount;
6934 break;
6935 }
6936 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006937 }
6938 }
6939
6940 // Allocate an empty array, will set length and content later.
6941 Handle<JSArray> result = Factory::NewJSArray(0);
6942
6943 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
6944 // If estimated number of elements is more than half of length, a
6945 // fixed array (fast case) is more time and space-efficient than a
6946 // dictionary.
6947 bool fast_case = (estimate_nof_elements * 2) >= result_length;
6948
6949 Handle<FixedArray> storage;
6950 if (fast_case) {
6951 // The backing storage array must have non-existing elements to
6952 // preserve holes across concat operations.
6953 storage = Factory::NewFixedArrayWithHoles(result_length);
6954
6955 } else {
6956 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
6957 uint32_t at_least_space_for = estimate_nof_elements +
6958 (estimate_nof_elements >> 2);
6959 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006960 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006961 }
6962
6963 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
6964
6965 ArrayConcatVisitor visitor(storage, result_length, fast_case);
6966
6967 IterateArguments(arguments, &visitor);
6968
6969 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00006970 // Please note the storage might have changed in the visitor.
6971 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006972
6973 return *result;
6974}
6975
6976
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006977// This will not allocate (flatten the string), but it may run
6978// very slowly for very deeply nested ConsStrings. For debugging use only.
6979static Object* Runtime_GlobalPrint(Arguments args) {
6980 NoHandleAllocation ha;
6981 ASSERT(args.length() == 1);
6982
6983 CONVERT_CHECKED(String, string, args[0]);
6984 StringInputBuffer buffer(string);
6985 while (buffer.has_more()) {
6986 uint16_t character = buffer.GetNext();
6987 PrintF("%c", character);
6988 }
6989 return string;
6990}
6991
ager@chromium.org5ec48922009-05-05 07:25:34 +00006992// Moves all own elements of an object, that are below a limit, to positions
6993// starting at zero. All undefined values are placed after non-undefined values,
6994// and are followed by non-existing element. Does not change the length
6995// property.
6996// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006997static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00006998 ASSERT(args.length() == 2);
6999 CONVERT_CHECKED(JSObject, object, args[0]);
7000 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7001 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007002}
7003
7004
7005// Move contents of argument 0 (an array) to argument 1 (an array)
7006static Object* Runtime_MoveArrayContents(Arguments args) {
7007 ASSERT(args.length() == 2);
7008 CONVERT_CHECKED(JSArray, from, args[0]);
7009 CONVERT_CHECKED(JSArray, to, args[1]);
7010 to->SetContent(FixedArray::cast(from->elements()));
7011 to->set_length(from->length());
7012 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007013 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007014 return to;
7015}
7016
7017
7018// How many elements does this array have?
7019static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7020 ASSERT(args.length() == 1);
7021 CONVERT_CHECKED(JSArray, array, args[0]);
7022 HeapObject* elements = array->elements();
7023 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007024 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007025 } else {
7026 return array->length();
7027 }
7028}
7029
7030
7031// Returns an array that tells you where in the [0, length) interval an array
7032// might have elements. Can either return keys or intervals. Keys can have
7033// gaps in (undefined). Intervals can also span over some undefined keys.
7034static Object* Runtime_GetArrayKeys(Arguments args) {
7035 ASSERT(args.length() == 2);
7036 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007037 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007038 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007039 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007040 // Create an array and get all the keys into it, then remove all the
7041 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007042 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007043 int keys_length = keys->length();
7044 for (int i = 0; i < keys_length; i++) {
7045 Object* key = keys->get(i);
7046 uint32_t index;
7047 if (!Array::IndexFromObject(key, &index) || index >= length) {
7048 // Zap invalid keys.
7049 keys->set_undefined(i);
7050 }
7051 }
7052 return *Factory::NewJSArrayWithElements(keys);
7053 } else {
7054 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7055 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007056 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00007057 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
7058 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007059 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007060 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007061 single_interval->set(1, *length_object);
7062 return *Factory::NewJSArrayWithElements(single_interval);
7063 }
7064}
7065
7066
7067// DefineAccessor takes an optional final argument which is the
7068// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7069// to the way accessors are implemented, it is set for both the getter
7070// and setter on the first call to DefineAccessor and ignored on
7071// subsequent calls.
7072static Object* Runtime_DefineAccessor(Arguments args) {
7073 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7074 // Compute attributes.
7075 PropertyAttributes attributes = NONE;
7076 if (args.length() == 5) {
7077 CONVERT_CHECKED(Smi, attrs, args[4]);
7078 int value = attrs->value();
7079 // Only attribute bits should be set.
7080 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7081 attributes = static_cast<PropertyAttributes>(value);
7082 }
7083
7084 CONVERT_CHECKED(JSObject, obj, args[0]);
7085 CONVERT_CHECKED(String, name, args[1]);
7086 CONVERT_CHECKED(Smi, flag, args[2]);
7087 CONVERT_CHECKED(JSFunction, fun, args[3]);
7088 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7089}
7090
7091
7092static Object* Runtime_LookupAccessor(Arguments args) {
7093 ASSERT(args.length() == 3);
7094 CONVERT_CHECKED(JSObject, obj, args[0]);
7095 CONVERT_CHECKED(String, name, args[1]);
7096 CONVERT_CHECKED(Smi, flag, args[2]);
7097 return obj->LookupAccessor(name, flag->value() == 0);
7098}
7099
7100
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007101#ifdef ENABLE_DEBUGGER_SUPPORT
7102static Object* Runtime_DebugBreak(Arguments args) {
7103 ASSERT(args.length() == 0);
7104 return Execution::DebugBreakHelper();
7105}
7106
7107
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108// Helper functions for wrapping and unwrapping stack frame ids.
7109static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007110 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007111 return Smi::FromInt(id >> 2);
7112}
7113
7114
7115static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7116 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7117}
7118
7119
7120// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007121// args[0]: debug event listener function to set or null or undefined for
7122// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007123// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007124static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007125 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007126 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7127 args[0]->IsUndefined() ||
7128 args[0]->IsNull());
7129 Handle<Object> callback = args.at<Object>(0);
7130 Handle<Object> data = args.at<Object>(1);
7131 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007132
7133 return Heap::undefined_value();
7134}
7135
7136
7137static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007138 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007139 StackGuard::DebugBreak();
7140 return Heap::undefined_value();
7141}
7142
7143
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007144static Object* DebugLookupResultValue(Object* receiver, String* name,
7145 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007146 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007147 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007148 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007149 case NORMAL:
7150 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007151 if (value->IsTheHole()) {
7152 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007153 }
7154 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007155 case FIELD:
7156 value =
7157 JSObject::cast(
7158 result->holder())->FastPropertyAt(result->GetFieldIndex());
7159 if (value->IsTheHole()) {
7160 return Heap::undefined_value();
7161 }
7162 return value;
7163 case CONSTANT_FUNCTION:
7164 return result->GetConstantFunction();
7165 case CALLBACKS: {
7166 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007167 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007168 value = receiver->GetPropertyWithCallback(
7169 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007170 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007171 value = Top::pending_exception();
7172 Top::clear_pending_exception();
7173 if (caught_exception != NULL) {
7174 *caught_exception = true;
7175 }
7176 }
7177 return value;
7178 } else {
7179 return Heap::undefined_value();
7180 }
7181 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007182 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007183 case MAP_TRANSITION:
7184 case CONSTANT_TRANSITION:
7185 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007186 return Heap::undefined_value();
7187 default:
7188 UNREACHABLE();
7189 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007190 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007191 return Heap::undefined_value();
7192}
7193
7194
ager@chromium.org32912102009-01-16 10:38:43 +00007195// Get debugger related details for an object property.
7196// args[0]: object holding property
7197// args[1]: name of the property
7198//
7199// The array returned contains the following information:
7200// 0: Property value
7201// 1: Property details
7202// 2: Property value is exception
7203// 3: Getter function if defined
7204// 4: Setter function if defined
7205// Items 2-4 are only filled if the property has either a getter or a setter
7206// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007207static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007208 HandleScope scope;
7209
7210 ASSERT(args.length() == 2);
7211
7212 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7213 CONVERT_ARG_CHECKED(String, name, 1);
7214
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007215 // Make sure to set the current context to the context before the debugger was
7216 // entered (if the debugger is entered). The reason for switching context here
7217 // is that for some property lookups (accessors and interceptors) callbacks
7218 // into the embedding application can occour, and the embedding application
7219 // could have the assumption that its own global context is the current
7220 // context and not some internal debugger context.
7221 SaveContext save;
7222 if (Debug::InDebugger()) {
7223 Top::set_context(*Debug::debugger_entry()->GetContext());
7224 }
7225
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007226 // Skip the global proxy as it has no properties and always delegates to the
7227 // real global object.
7228 if (obj->IsJSGlobalProxy()) {
7229 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
7230 }
7231
7232
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007233 // Check if the name is trivially convertible to an index and get the element
7234 // if so.
7235 uint32_t index;
7236 if (name->AsArrayIndex(&index)) {
7237 Handle<FixedArray> details = Factory::NewFixedArray(2);
7238 details->set(0, Runtime::GetElementOrCharAt(obj, index));
7239 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
7240 return *Factory::NewJSArrayWithElements(details);
7241 }
7242
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007243 // Find the number of objects making up this.
7244 int length = LocalPrototypeChainLength(*obj);
7245
7246 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007247 Handle<JSObject> jsproto = obj;
7248 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007249 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007250 jsproto->LocalLookup(*name, &result);
7251 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007252 // LookupResult is not GC safe as it holds raw object pointers.
7253 // GC can happen later in this code so put the required fields into
7254 // local variables using handles when required for later use.
7255 PropertyType result_type = result.type();
7256 Handle<Object> result_callback_obj;
7257 if (result_type == CALLBACKS) {
7258 result_callback_obj = Handle<Object>(result.GetCallbackObject());
7259 }
7260 Smi* property_details = result.GetPropertyDetails().AsSmi();
7261 // DebugLookupResultValue can cause GC so details from LookupResult needs
7262 // to be copied to handles before this.
7263 bool caught_exception = false;
7264 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
7265 &caught_exception);
7266 if (raw_value->IsFailure()) return raw_value;
7267 Handle<Object> value(raw_value);
7268
7269 // If the callback object is a fixed array then it contains JavaScript
7270 // getter and/or setter.
7271 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
7272 result_callback_obj->IsFixedArray();
7273 Handle<FixedArray> details =
7274 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
7275 details->set(0, *value);
7276 details->set(1, property_details);
7277 if (hasJavaScriptAccessors) {
7278 details->set(2,
7279 caught_exception ? Heap::true_value()
7280 : Heap::false_value());
7281 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
7282 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
7283 }
7284
7285 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007286 }
7287 if (i < length - 1) {
7288 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
7289 }
7290 }
7291
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007292 return Heap::undefined_value();
7293}
7294
7295
7296static Object* Runtime_DebugGetProperty(Arguments args) {
7297 HandleScope scope;
7298
7299 ASSERT(args.length() == 2);
7300
7301 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7302 CONVERT_ARG_CHECKED(String, name, 1);
7303
7304 LookupResult result;
7305 obj->Lookup(*name, &result);
7306 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007307 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007308 }
7309 return Heap::undefined_value();
7310}
7311
7312
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007313// Return the property type calculated from the property details.
7314// args[0]: smi with property details.
7315static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
7316 ASSERT(args.length() == 1);
7317 CONVERT_CHECKED(Smi, details, args[0]);
7318 PropertyType type = PropertyDetails(details).type();
7319 return Smi::FromInt(static_cast<int>(type));
7320}
7321
7322
7323// Return the property attribute calculated from the property details.
7324// args[0]: smi with property details.
7325static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
7326 ASSERT(args.length() == 1);
7327 CONVERT_CHECKED(Smi, details, args[0]);
7328 PropertyAttributes attributes = PropertyDetails(details).attributes();
7329 return Smi::FromInt(static_cast<int>(attributes));
7330}
7331
7332
7333// Return the property insertion index calculated from the property details.
7334// args[0]: smi with property details.
7335static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
7336 ASSERT(args.length() == 1);
7337 CONVERT_CHECKED(Smi, details, args[0]);
7338 int index = PropertyDetails(details).index();
7339 return Smi::FromInt(index);
7340}
7341
7342
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007343// Return property value from named interceptor.
7344// args[0]: object
7345// args[1]: property name
7346static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
7347 HandleScope scope;
7348 ASSERT(args.length() == 2);
7349 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7350 RUNTIME_ASSERT(obj->HasNamedInterceptor());
7351 CONVERT_ARG_CHECKED(String, name, 1);
7352
7353 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007354 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007355}
7356
7357
7358// Return element value from indexed interceptor.
7359// args[0]: object
7360// args[1]: index
7361static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
7362 HandleScope scope;
7363 ASSERT(args.length() == 2);
7364 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7365 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
7366 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
7367
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007368 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007369}
7370
7371
7372static Object* Runtime_CheckExecutionState(Arguments args) {
7373 ASSERT(args.length() >= 1);
7374 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00007375 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007376 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007377 return Top::Throw(Heap::illegal_execution_state_symbol());
7378 }
7379
7380 return Heap::true_value();
7381}
7382
7383
7384static Object* Runtime_GetFrameCount(Arguments args) {
7385 HandleScope scope;
7386 ASSERT(args.length() == 1);
7387
7388 // Check arguments.
7389 Object* result = Runtime_CheckExecutionState(args);
7390 if (result->IsFailure()) return result;
7391
7392 // Count all frames which are relevant to debugging stack trace.
7393 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007394 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00007395 if (id == StackFrame::NO_ID) {
7396 // If there is no JavaScript stack frame count is 0.
7397 return Smi::FromInt(0);
7398 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007399 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
7400 return Smi::FromInt(n);
7401}
7402
7403
7404static const int kFrameDetailsFrameIdIndex = 0;
7405static const int kFrameDetailsReceiverIndex = 1;
7406static const int kFrameDetailsFunctionIndex = 2;
7407static const int kFrameDetailsArgumentCountIndex = 3;
7408static const int kFrameDetailsLocalCountIndex = 4;
7409static const int kFrameDetailsSourcePositionIndex = 5;
7410static const int kFrameDetailsConstructCallIndex = 6;
7411static const int kFrameDetailsDebuggerFrameIndex = 7;
7412static const int kFrameDetailsFirstDynamicIndex = 8;
7413
7414// Return an array with frame details
7415// args[0]: number: break id
7416// args[1]: number: frame index
7417//
7418// The array returned contains the following information:
7419// 0: Frame id
7420// 1: Receiver
7421// 2: Function
7422// 3: Argument count
7423// 4: Local count
7424// 5: Source position
7425// 6: Constructor call
7426// 7: Debugger frame
7427// Arguments name, value
7428// Locals name, value
7429static Object* Runtime_GetFrameDetails(Arguments args) {
7430 HandleScope scope;
7431 ASSERT(args.length() == 2);
7432
7433 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007434 Object* check = Runtime_CheckExecutionState(args);
7435 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007436 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
7437
7438 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00007439 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00007440 if (id == StackFrame::NO_ID) {
7441 // If there are no JavaScript stack frames return undefined.
7442 return Heap::undefined_value();
7443 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007444 int count = 0;
7445 JavaScriptFrameIterator it(id);
7446 for (; !it.done(); it.Advance()) {
7447 if (count == index) break;
7448 count++;
7449 }
7450 if (it.done()) return Heap::undefined_value();
7451
7452 // Traverse the saved contexts chain to find the active context for the
7453 // selected frame.
7454 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007455 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007456 save = save->prev();
7457 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007458 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007459
7460 // Get the frame id.
7461 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
7462
7463 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00007464 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007465
7466 // Check for constructor frame.
7467 bool constructor = it.frame()->IsConstructor();
7468
7469 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00007470 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007471 ScopeInfo<> info(*code);
7472
7473 // Get the context.
7474 Handle<Context> context(Context::cast(it.frame()->context()));
7475
7476 // Get the locals names and values into a temporary array.
7477 //
7478 // TODO(1240907): Hide compiler-introduced stack variables
7479 // (e.g. .result)? For users of the debugger, they will probably be
7480 // confusing.
7481 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
7482 for (int i = 0; i < info.NumberOfLocals(); i++) {
7483 // Name of the local.
7484 locals->set(i * 2, *info.LocalName(i));
7485
7486 // Fetch the value of the local - either from the stack or from a
7487 // heap-allocated context.
7488 if (i < info.number_of_stack_slots()) {
7489 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
7490 } else {
7491 Handle<String> name = info.LocalName(i);
7492 // Traverse the context chain to the function context as all local
7493 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007494 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007495 context = Handle<Context>(context->previous());
7496 }
7497 ASSERT(context->is_function_context());
7498 locals->set(i * 2 + 1,
7499 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
7500 NULL)));
7501 }
7502 }
7503
7504 // Now advance to the arguments adapter frame (if any). If contains all
7505 // the provided parameters and
7506
7507 // Now advance to the arguments adapter frame (if any). It contains all
7508 // the provided parameters whereas the function frame always have the number
7509 // of arguments matching the functions parameters. The rest of the
7510 // information (except for what is collected above) is the same.
7511 it.AdvanceToArgumentsFrame();
7512
7513 // Find the number of arguments to fill. At least fill the number of
7514 // parameters for the function and fill more if more parameters are provided.
7515 int argument_count = info.number_of_parameters();
7516 if (argument_count < it.frame()->GetProvidedParametersCount()) {
7517 argument_count = it.frame()->GetProvidedParametersCount();
7518 }
7519
7520 // Calculate the size of the result.
7521 int details_size = kFrameDetailsFirstDynamicIndex +
7522 2 * (argument_count + info.NumberOfLocals());
7523 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
7524
7525 // Add the frame id.
7526 details->set(kFrameDetailsFrameIdIndex, *frame_id);
7527
7528 // Add the function (same as in function frame).
7529 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
7530
7531 // Add the arguments count.
7532 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
7533
7534 // Add the locals count
7535 details->set(kFrameDetailsLocalCountIndex,
7536 Smi::FromInt(info.NumberOfLocals()));
7537
7538 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00007539 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007540 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
7541 } else {
7542 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
7543 }
7544
7545 // Add the constructor information.
7546 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
7547
7548 // Add information on whether this frame is invoked in the debugger context.
7549 details->set(kFrameDetailsDebuggerFrameIndex,
7550 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
7551
7552 // Fill the dynamic part.
7553 int details_index = kFrameDetailsFirstDynamicIndex;
7554
7555 // Add arguments name and value.
7556 for (int i = 0; i < argument_count; i++) {
7557 // Name of the argument.
7558 if (i < info.number_of_parameters()) {
7559 details->set(details_index++, *info.parameter_name(i));
7560 } else {
7561 details->set(details_index++, Heap::undefined_value());
7562 }
7563
7564 // Parameter value.
7565 if (i < it.frame()->GetProvidedParametersCount()) {
7566 details->set(details_index++, it.frame()->GetParameter(i));
7567 } else {
7568 details->set(details_index++, Heap::undefined_value());
7569 }
7570 }
7571
7572 // Add locals name and value from the temporary copy from the function frame.
7573 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
7574 details->set(details_index++, locals->get(i));
7575 }
7576
7577 // Add the receiver (same as in function frame).
7578 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
7579 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
7580 Handle<Object> receiver(it.frame()->receiver());
7581 if (!receiver->IsJSObject()) {
7582 // If the receiver is NOT a JSObject we have hit an optimization
7583 // where a value object is not converted into a wrapped JS objects.
7584 // To hide this optimization from the debugger, we wrap the receiver
7585 // by creating correct wrapper object based on the calling frame's
7586 // global context.
7587 it.Advance();
7588 Handle<Context> calling_frames_global_context(
7589 Context::cast(Context::cast(it.frame()->context())->global_context()));
7590 receiver = Factory::ToObject(receiver, calling_frames_global_context);
7591 }
7592 details->set(kFrameDetailsReceiverIndex, *receiver);
7593
7594 ASSERT_EQ(details_size, details_index);
7595 return *Factory::NewJSArrayWithElements(details);
7596}
7597
7598
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007599// Copy all the context locals into an object used to materialize a scope.
7600static void CopyContextLocalsToScopeObject(Handle<Code> code,
7601 ScopeInfo<>& scope_info,
7602 Handle<Context> context,
7603 Handle<JSObject> scope_object) {
7604 // Fill all context locals to the context extension.
7605 for (int i = Context::MIN_CONTEXT_SLOTS;
7606 i < scope_info.number_of_context_slots();
7607 i++) {
7608 int context_index =
7609 ScopeInfo<>::ContextSlotIndex(*code,
7610 *scope_info.context_slot_name(i),
7611 NULL);
7612
7613 // Don't include the arguments shadow (.arguments) context variable.
7614 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
7615 SetProperty(scope_object,
7616 scope_info.context_slot_name(i),
7617 Handle<Object>(context->get(context_index)), NONE);
7618 }
7619 }
7620}
7621
7622
7623// Create a plain JSObject which materializes the local scope for the specified
7624// frame.
7625static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
7626 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7627 Handle<Code> code(function->code());
7628 ScopeInfo<> scope_info(*code);
7629
7630 // Allocate and initialize a JSObject with all the arguments, stack locals
7631 // heap locals and extension properties of the debugged function.
7632 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
7633
7634 // First fill all parameters.
7635 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
7636 SetProperty(local_scope,
7637 scope_info.parameter_name(i),
7638 Handle<Object>(frame->GetParameter(i)), NONE);
7639 }
7640
7641 // Second fill all stack locals.
7642 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
7643 SetProperty(local_scope,
7644 scope_info.stack_slot_name(i),
7645 Handle<Object>(frame->GetExpression(i)), NONE);
7646 }
7647
7648 // Third fill all context locals.
7649 Handle<Context> frame_context(Context::cast(frame->context()));
7650 Handle<Context> function_context(frame_context->fcontext());
7651 CopyContextLocalsToScopeObject(code, scope_info,
7652 function_context, local_scope);
7653
7654 // Finally copy any properties from the function context extension. This will
7655 // be variables introduced by eval.
7656 if (function_context->closure() == *function) {
7657 if (function_context->has_extension() &&
7658 !function_context->IsGlobalContext()) {
7659 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007660 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007661 for (int i = 0; i < keys->length(); i++) {
7662 // Names of variables introduced by eval are strings.
7663 ASSERT(keys->get(i)->IsString());
7664 Handle<String> key(String::cast(keys->get(i)));
7665 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
7666 }
7667 }
7668 }
7669 return local_scope;
7670}
7671
7672
7673// Create a plain JSObject which materializes the closure content for the
7674// context.
7675static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
7676 ASSERT(context->is_function_context());
7677
7678 Handle<Code> code(context->closure()->code());
7679 ScopeInfo<> scope_info(*code);
7680
7681 // Allocate and initialize a JSObject with all the content of theis function
7682 // closure.
7683 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
7684
7685 // Check whether the arguments shadow object exists.
7686 int arguments_shadow_index =
7687 ScopeInfo<>::ContextSlotIndex(*code,
7688 Heap::arguments_shadow_symbol(),
7689 NULL);
7690 if (arguments_shadow_index >= 0) {
7691 // In this case all the arguments are available in the arguments shadow
7692 // object.
7693 Handle<JSObject> arguments_shadow(
7694 JSObject::cast(context->get(arguments_shadow_index)));
7695 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
7696 SetProperty(closure_scope,
7697 scope_info.parameter_name(i),
7698 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
7699 }
7700 }
7701
7702 // Fill all context locals to the context extension.
7703 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
7704
7705 // Finally copy any properties from the function context extension. This will
7706 // be variables introduced by eval.
7707 if (context->has_extension()) {
7708 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007709 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007710 for (int i = 0; i < keys->length(); i++) {
7711 // Names of variables introduced by eval are strings.
7712 ASSERT(keys->get(i)->IsString());
7713 Handle<String> key(String::cast(keys->get(i)));
7714 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
7715 }
7716 }
7717
7718 return closure_scope;
7719}
7720
7721
7722// Iterate over the actual scopes visible from a stack frame. All scopes are
7723// backed by an actual context except the local scope, which is inserted
7724// "artifically" in the context chain.
7725class ScopeIterator {
7726 public:
7727 enum ScopeType {
7728 ScopeTypeGlobal = 0,
7729 ScopeTypeLocal,
7730 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00007731 ScopeTypeClosure,
7732 // Every catch block contains an implicit with block (its parameter is
7733 // a JSContextExtensionObject) that extends current scope with a variable
7734 // holding exception object. Such with blocks are treated as scopes of their
7735 // own type.
7736 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007737 };
7738
7739 explicit ScopeIterator(JavaScriptFrame* frame)
7740 : frame_(frame),
7741 function_(JSFunction::cast(frame->function())),
7742 context_(Context::cast(frame->context())),
7743 local_done_(false),
7744 at_local_(false) {
7745
7746 // Check whether the first scope is actually a local scope.
7747 if (context_->IsGlobalContext()) {
7748 // If there is a stack slot for .result then this local scope has been
7749 // created for evaluating top level code and it is not a real local scope.
7750 // Checking for the existence of .result seems fragile, but the scope info
7751 // saved with the code object does not otherwise have that information.
7752 Handle<Code> code(function_->code());
7753 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
7754 at_local_ = index < 0;
7755 } else if (context_->is_function_context()) {
7756 at_local_ = true;
7757 }
7758 }
7759
7760 // More scopes?
7761 bool Done() { return context_.is_null(); }
7762
7763 // Move to the next scope.
7764 void Next() {
7765 // If at a local scope mark the local scope as passed.
7766 if (at_local_) {
7767 at_local_ = false;
7768 local_done_ = true;
7769
7770 // If the current context is not associated with the local scope the
7771 // current context is the next real scope, so don't move to the next
7772 // context in this case.
7773 if (context_->closure() != *function_) {
7774 return;
7775 }
7776 }
7777
7778 // The global scope is always the last in the chain.
7779 if (context_->IsGlobalContext()) {
7780 context_ = Handle<Context>();
7781 return;
7782 }
7783
7784 // Move to the next context.
7785 if (context_->is_function_context()) {
7786 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
7787 } else {
7788 context_ = Handle<Context>(context_->previous());
7789 }
7790
7791 // If passing the local scope indicate that the current scope is now the
7792 // local scope.
7793 if (!local_done_ &&
7794 (context_->IsGlobalContext() || (context_->is_function_context()))) {
7795 at_local_ = true;
7796 }
7797 }
7798
7799 // Return the type of the current scope.
7800 int Type() {
7801 if (at_local_) {
7802 return ScopeTypeLocal;
7803 }
7804 if (context_->IsGlobalContext()) {
7805 ASSERT(context_->global()->IsGlobalObject());
7806 return ScopeTypeGlobal;
7807 }
7808 if (context_->is_function_context()) {
7809 return ScopeTypeClosure;
7810 }
7811 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00007812 // Current scope is either an explicit with statement or a with statement
7813 // implicitely generated for a catch block.
7814 // If the extension object here is a JSContextExtensionObject then
7815 // current with statement is one frome a catch block otherwise it's a
7816 // regular with statement.
7817 if (context_->extension()->IsJSContextExtensionObject()) {
7818 return ScopeTypeCatch;
7819 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007820 return ScopeTypeWith;
7821 }
7822
7823 // Return the JavaScript object with the content of the current scope.
7824 Handle<JSObject> ScopeObject() {
7825 switch (Type()) {
7826 case ScopeIterator::ScopeTypeGlobal:
7827 return Handle<JSObject>(CurrentContext()->global());
7828 break;
7829 case ScopeIterator::ScopeTypeLocal:
7830 // Materialize the content of the local scope into a JSObject.
7831 return MaterializeLocalScope(frame_);
7832 break;
7833 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00007834 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007835 // Return the with object.
7836 return Handle<JSObject>(CurrentContext()->extension());
7837 break;
7838 case ScopeIterator::ScopeTypeClosure:
7839 // Materialize the content of the closure scope into a JSObject.
7840 return MaterializeClosure(CurrentContext());
7841 break;
7842 }
7843 UNREACHABLE();
7844 return Handle<JSObject>();
7845 }
7846
7847 // Return the context for this scope. For the local context there might not
7848 // be an actual context.
7849 Handle<Context> CurrentContext() {
7850 if (at_local_ && context_->closure() != *function_) {
7851 return Handle<Context>();
7852 }
7853 return context_;
7854 }
7855
7856#ifdef DEBUG
7857 // Debug print of the content of the current scope.
7858 void DebugPrint() {
7859 switch (Type()) {
7860 case ScopeIterator::ScopeTypeGlobal:
7861 PrintF("Global:\n");
7862 CurrentContext()->Print();
7863 break;
7864
7865 case ScopeIterator::ScopeTypeLocal: {
7866 PrintF("Local:\n");
7867 Handle<Code> code(function_->code());
7868 ScopeInfo<> scope_info(*code);
7869 scope_info.Print();
7870 if (!CurrentContext().is_null()) {
7871 CurrentContext()->Print();
7872 if (CurrentContext()->has_extension()) {
7873 Handle<JSObject> extension =
7874 Handle<JSObject>(CurrentContext()->extension());
7875 if (extension->IsJSContextExtensionObject()) {
7876 extension->Print();
7877 }
7878 }
7879 }
7880 break;
7881 }
7882
7883 case ScopeIterator::ScopeTypeWith: {
7884 PrintF("With:\n");
7885 Handle<JSObject> extension =
7886 Handle<JSObject>(CurrentContext()->extension());
7887 extension->Print();
7888 break;
7889 }
7890
ager@chromium.orga1645e22009-09-09 19:27:10 +00007891 case ScopeIterator::ScopeTypeCatch: {
7892 PrintF("Catch:\n");
7893 Handle<JSObject> extension =
7894 Handle<JSObject>(CurrentContext()->extension());
7895 extension->Print();
7896 break;
7897 }
7898
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007899 case ScopeIterator::ScopeTypeClosure: {
7900 PrintF("Closure:\n");
7901 CurrentContext()->Print();
7902 if (CurrentContext()->has_extension()) {
7903 Handle<JSObject> extension =
7904 Handle<JSObject>(CurrentContext()->extension());
7905 if (extension->IsJSContextExtensionObject()) {
7906 extension->Print();
7907 }
7908 }
7909 break;
7910 }
7911
7912 default:
7913 UNREACHABLE();
7914 }
7915 PrintF("\n");
7916 }
7917#endif
7918
7919 private:
7920 JavaScriptFrame* frame_;
7921 Handle<JSFunction> function_;
7922 Handle<Context> context_;
7923 bool local_done_;
7924 bool at_local_;
7925
7926 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
7927};
7928
7929
7930static Object* Runtime_GetScopeCount(Arguments args) {
7931 HandleScope scope;
7932 ASSERT(args.length() == 2);
7933
7934 // Check arguments.
7935 Object* check = Runtime_CheckExecutionState(args);
7936 if (check->IsFailure()) return check;
7937 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7938
7939 // Get the frame where the debugging is performed.
7940 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7941 JavaScriptFrameIterator it(id);
7942 JavaScriptFrame* frame = it.frame();
7943
7944 // Count the visible scopes.
7945 int n = 0;
7946 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
7947 n++;
7948 }
7949
7950 return Smi::FromInt(n);
7951}
7952
7953
7954static const int kScopeDetailsTypeIndex = 0;
7955static const int kScopeDetailsObjectIndex = 1;
7956static const int kScopeDetailsSize = 2;
7957
7958// Return an array with scope details
7959// args[0]: number: break id
7960// args[1]: number: frame index
7961// args[2]: number: scope index
7962//
7963// The array returned contains the following information:
7964// 0: Scope type
7965// 1: Scope object
7966static Object* Runtime_GetScopeDetails(Arguments args) {
7967 HandleScope scope;
7968 ASSERT(args.length() == 3);
7969
7970 // Check arguments.
7971 Object* check = Runtime_CheckExecutionState(args);
7972 if (check->IsFailure()) return check;
7973 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7974 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
7975
7976 // Get the frame where the debugging is performed.
7977 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7978 JavaScriptFrameIterator frame_it(id);
7979 JavaScriptFrame* frame = frame_it.frame();
7980
7981 // Find the requested scope.
7982 int n = 0;
7983 ScopeIterator it(frame);
7984 for (; !it.Done() && n < index; it.Next()) {
7985 n++;
7986 }
7987 if (it.Done()) {
7988 return Heap::undefined_value();
7989 }
7990
7991 // Calculate the size of the result.
7992 int details_size = kScopeDetailsSize;
7993 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
7994
7995 // Fill in scope details.
7996 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
7997 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
7998
7999 return *Factory::NewJSArrayWithElements(details);
8000}
8001
8002
8003static Object* Runtime_DebugPrintScopes(Arguments args) {
8004 HandleScope scope;
8005 ASSERT(args.length() == 0);
8006
8007#ifdef DEBUG
8008 // Print the scopes for the top frame.
8009 StackFrameLocator locator;
8010 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8011 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8012 it.DebugPrint();
8013 }
8014#endif
8015 return Heap::undefined_value();
8016}
8017
8018
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008019static Object* Runtime_GetCFrames(Arguments args) {
8020 HandleScope scope;
8021 ASSERT(args.length() == 1);
8022 Object* result = Runtime_CheckExecutionState(args);
8023 if (result->IsFailure()) return result;
8024
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008025#if V8_HOST_ARCH_64_BIT
8026 UNIMPLEMENTED();
8027 return Heap::undefined_value();
8028#else
8029
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008030 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008031 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8032 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008033 if (frames_count == OS::kStackWalkError) {
8034 return Heap::undefined_value();
8035 }
8036
8037 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8038 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8039 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8040 for (int i = 0; i < frames_count; i++) {
8041 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8042 frame_value->SetProperty(
8043 *address_str,
8044 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8045 NONE);
8046
8047 // Get the stack walk text for this frame.
8048 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008049 int frame_text_length = StrLength(frames[i].text);
8050 if (frame_text_length > 0) {
8051 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008052 frame_text = Factory::NewStringFromAscii(str);
8053 }
8054
8055 if (!frame_text.is_null()) {
8056 frame_value->SetProperty(*text_str, *frame_text, NONE);
8057 }
8058
8059 frames_array->set(i, *frame_value);
8060 }
8061 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008062#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008063}
8064
8065
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008066static Object* Runtime_GetThreadCount(Arguments args) {
8067 HandleScope scope;
8068 ASSERT(args.length() == 1);
8069
8070 // Check arguments.
8071 Object* result = Runtime_CheckExecutionState(args);
8072 if (result->IsFailure()) return result;
8073
8074 // Count all archived V8 threads.
8075 int n = 0;
8076 for (ThreadState* thread = ThreadState::FirstInUse();
8077 thread != NULL;
8078 thread = thread->Next()) {
8079 n++;
8080 }
8081
8082 // Total number of threads is current thread and archived threads.
8083 return Smi::FromInt(n + 1);
8084}
8085
8086
8087static const int kThreadDetailsCurrentThreadIndex = 0;
8088static const int kThreadDetailsThreadIdIndex = 1;
8089static const int kThreadDetailsSize = 2;
8090
8091// Return an array with thread details
8092// args[0]: number: break id
8093// args[1]: number: thread index
8094//
8095// The array returned contains the following information:
8096// 0: Is current thread?
8097// 1: Thread id
8098static Object* Runtime_GetThreadDetails(Arguments args) {
8099 HandleScope scope;
8100 ASSERT(args.length() == 2);
8101
8102 // Check arguments.
8103 Object* check = Runtime_CheckExecutionState(args);
8104 if (check->IsFailure()) return check;
8105 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8106
8107 // Allocate array for result.
8108 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8109
8110 // Thread index 0 is current thread.
8111 if (index == 0) {
8112 // Fill the details.
8113 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8114 details->set(kThreadDetailsThreadIdIndex,
8115 Smi::FromInt(ThreadManager::CurrentId()));
8116 } else {
8117 // Find the thread with the requested index.
8118 int n = 1;
8119 ThreadState* thread = ThreadState::FirstInUse();
8120 while (index != n && thread != NULL) {
8121 thread = thread->Next();
8122 n++;
8123 }
8124 if (thread == NULL) {
8125 return Heap::undefined_value();
8126 }
8127
8128 // Fill the details.
8129 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8130 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8131 }
8132
8133 // Convert to JS array and return.
8134 return *Factory::NewJSArrayWithElements(details);
8135}
8136
8137
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008138static Object* Runtime_GetBreakLocations(Arguments args) {
8139 HandleScope scope;
8140 ASSERT(args.length() == 1);
8141
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008142 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8143 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008144 // Find the number of break points
8145 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8146 if (break_locations->IsUndefined()) return Heap::undefined_value();
8147 // Return array as JS array
8148 return *Factory::NewJSArrayWithElements(
8149 Handle<FixedArray>::cast(break_locations));
8150}
8151
8152
8153// Set a break point in a function
8154// args[0]: function
8155// args[1]: number: break source position (within the function source)
8156// args[2]: number: break point object
8157static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8158 HandleScope scope;
8159 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008160 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8161 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008162 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8163 RUNTIME_ASSERT(source_position >= 0);
8164 Handle<Object> break_point_object_arg = args.at<Object>(2);
8165
8166 // Set break point.
8167 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8168
8169 return Heap::undefined_value();
8170}
8171
8172
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008173Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8174 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008175 // Iterate the heap looking for SharedFunctionInfo generated from the
8176 // script. The inner most SharedFunctionInfo containing the source position
8177 // for the requested break point is found.
8178 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8179 // which is found is not compiled it is compiled and the heap is iterated
8180 // again as the compilation might create inner functions from the newly
8181 // compiled function and the actual requested break point might be in one of
8182 // these functions.
8183 bool done = false;
8184 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008185 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008186 Handle<SharedFunctionInfo> target;
8187 // The current candidate for the last function in script:
8188 Handle<SharedFunctionInfo> last;
8189 while (!done) {
8190 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008191 for (HeapObject* obj = iterator.next();
8192 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008193 if (obj->IsSharedFunctionInfo()) {
8194 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
8195 if (shared->script() == *script) {
8196 // If the SharedFunctionInfo found has the requested script data and
8197 // contains the source position it is a candidate.
8198 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00008199 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008200 start_position = shared->start_position();
8201 }
8202 if (start_position <= position &&
8203 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00008204 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008205 // candidate this is the new candidate.
8206 if (target.is_null()) {
8207 target_start_position = start_position;
8208 target = shared;
8209 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00008210 if (target_start_position == start_position &&
8211 shared->end_position() == target->end_position()) {
8212 // If a top-level function contain only one function
8213 // declartion the source for the top-level and the function is
8214 // the same. In that case prefer the non top-level function.
8215 if (!shared->is_toplevel()) {
8216 target_start_position = start_position;
8217 target = shared;
8218 }
8219 } else if (target_start_position <= start_position &&
8220 shared->end_position() <= target->end_position()) {
8221 // This containment check includes equality as a function inside
8222 // a top-level function can share either start or end position
8223 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008224 target_start_position = start_position;
8225 target = shared;
8226 }
8227 }
8228 }
8229
8230 // Keep track of the last function in the script.
8231 if (last.is_null() ||
8232 shared->end_position() > last->start_position()) {
8233 last = shared;
8234 }
8235 }
8236 }
8237 }
8238
8239 // Make sure some candidate is selected.
8240 if (target.is_null()) {
8241 if (!last.is_null()) {
8242 // Position after the last function - use last.
8243 target = last;
8244 } else {
8245 // Unable to find function - possibly script without any function.
8246 return Heap::undefined_value();
8247 }
8248 }
8249
8250 // If the candidate found is compiled we are done. NOTE: when lazy
8251 // compilation of inner functions is introduced some additional checking
8252 // needs to be done here to compile inner functions.
8253 done = target->is_compiled();
8254 if (!done) {
8255 // If the candidate is not compiled compile it to reveal any inner
8256 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008257 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008258 }
8259 }
8260
8261 return *target;
8262}
8263
8264
8265// Change the state of a break point in a script. NOTE: Regarding performance
8266// see the NOTE for GetScriptFromScriptData.
8267// args[0]: script to set break point in
8268// args[1]: number: break source position (within the script source)
8269// args[2]: number: break point object
8270static Object* Runtime_SetScriptBreakPoint(Arguments args) {
8271 HandleScope scope;
8272 ASSERT(args.length() == 3);
8273 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
8274 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8275 RUNTIME_ASSERT(source_position >= 0);
8276 Handle<Object> break_point_object_arg = args.at<Object>(2);
8277
8278 // Get the script from the script wrapper.
8279 RUNTIME_ASSERT(wrapper->value()->IsScript());
8280 Handle<Script> script(Script::cast(wrapper->value()));
8281
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008282 Object* result = Runtime::FindSharedFunctionInfoInScript(
8283 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008284 if (!result->IsUndefined()) {
8285 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
8286 // Find position within function. The script position might be before the
8287 // source position of the first function.
8288 int position;
8289 if (shared->start_position() > source_position) {
8290 position = 0;
8291 } else {
8292 position = source_position - shared->start_position();
8293 }
8294 Debug::SetBreakPoint(shared, position, break_point_object_arg);
8295 }
8296 return Heap::undefined_value();
8297}
8298
8299
8300// Clear a break point
8301// args[0]: number: break point object
8302static Object* Runtime_ClearBreakPoint(Arguments args) {
8303 HandleScope scope;
8304 ASSERT(args.length() == 1);
8305 Handle<Object> break_point_object_arg = args.at<Object>(0);
8306
8307 // Clear break point.
8308 Debug::ClearBreakPoint(break_point_object_arg);
8309
8310 return Heap::undefined_value();
8311}
8312
8313
8314// Change the state of break on exceptions
8315// args[0]: boolean indicating uncaught exceptions
8316// args[1]: boolean indicating on/off
8317static Object* Runtime_ChangeBreakOnException(Arguments args) {
8318 HandleScope scope;
8319 ASSERT(args.length() == 2);
8320 ASSERT(args[0]->IsNumber());
8321 ASSERT(args[1]->IsBoolean());
8322
8323 // Update break point state
8324 ExceptionBreakType type =
8325 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
8326 bool enable = args[1]->ToBoolean()->IsTrue();
8327 Debug::ChangeBreakOnException(type, enable);
8328 return Heap::undefined_value();
8329}
8330
8331
8332// Prepare for stepping
8333// args[0]: break id for checking execution state
8334// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00008335// args[2]: number of times to perform the step, for step out it is the number
8336// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008337static Object* Runtime_PrepareStep(Arguments args) {
8338 HandleScope scope;
8339 ASSERT(args.length() == 3);
8340 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008341 Object* check = Runtime_CheckExecutionState(args);
8342 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008343 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
8344 return Top::Throw(Heap::illegal_argument_symbol());
8345 }
8346
8347 // Get the step action and check validity.
8348 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
8349 if (step_action != StepIn &&
8350 step_action != StepNext &&
8351 step_action != StepOut &&
8352 step_action != StepInMin &&
8353 step_action != StepMin) {
8354 return Top::Throw(Heap::illegal_argument_symbol());
8355 }
8356
8357 // Get the number of steps.
8358 int step_count = NumberToInt32(args[2]);
8359 if (step_count < 1) {
8360 return Top::Throw(Heap::illegal_argument_symbol());
8361 }
8362
ager@chromium.orga1645e22009-09-09 19:27:10 +00008363 // Clear all current stepping setup.
8364 Debug::ClearStepping();
8365
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008366 // Prepare step.
8367 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
8368 return Heap::undefined_value();
8369}
8370
8371
8372// Clear all stepping set by PrepareStep.
8373static Object* Runtime_ClearStepping(Arguments args) {
8374 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00008375 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008376 Debug::ClearStepping();
8377 return Heap::undefined_value();
8378}
8379
8380
8381// Creates a copy of the with context chain. The copy of the context chain is
8382// is linked to the function context supplied.
8383static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
8384 Handle<Context> function_context) {
8385 // At the bottom of the chain. Return the function context to link to.
8386 if (context_chain->is_function_context()) {
8387 return function_context;
8388 }
8389
8390 // Recursively copy the with contexts.
8391 Handle<Context> previous(context_chain->previous());
8392 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
8393 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00008394 CopyWithContextChain(function_context, previous),
8395 extension,
8396 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008397}
8398
8399
8400// Helper function to find or create the arguments object for
8401// Runtime_DebugEvaluate.
8402static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
8403 Handle<JSFunction> function,
8404 Handle<Code> code,
8405 const ScopeInfo<>* sinfo,
8406 Handle<Context> function_context) {
8407 // Try to find the value of 'arguments' to pass as parameter. If it is not
8408 // found (that is the debugged function does not reference 'arguments' and
8409 // does not support eval) then create an 'arguments' object.
8410 int index;
8411 if (sinfo->number_of_stack_slots() > 0) {
8412 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
8413 if (index != -1) {
8414 return Handle<Object>(frame->GetExpression(index));
8415 }
8416 }
8417
8418 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
8419 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
8420 NULL);
8421 if (index != -1) {
8422 return Handle<Object>(function_context->get(index));
8423 }
8424 }
8425
8426 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008427 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
8428 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008429
8430 AssertNoAllocation no_gc;
8431 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008432 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00008433 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008434 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008435 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008436 return arguments;
8437}
8438
8439
8440// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00008441// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008442// extension part has all the parameters and locals of the function on the
8443// stack frame. A function which calls eval with the code to evaluate is then
8444// compiled in this context and called in this context. As this context
8445// replaces the context of the function on the stack frame a new (empty)
8446// function is created as well to be used as the closure for the context.
8447// This function and the context acts as replacements for the function on the
8448// stack frame presenting the same view of the values of parameters and
8449// local variables as if the piece of JavaScript was evaluated at the point
8450// where the function on the stack frame is currently stopped.
8451static Object* Runtime_DebugEvaluate(Arguments args) {
8452 HandleScope scope;
8453
8454 // Check the execution state and decode arguments frame and source to be
8455 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00008456 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008457 Object* check_result = Runtime_CheckExecutionState(args);
8458 if (check_result->IsFailure()) return check_result;
8459 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8460 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00008461 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
8462
8463 // Handle the processing of break.
8464 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008465
8466 // Get the frame where the debugging is performed.
8467 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8468 JavaScriptFrameIterator it(id);
8469 JavaScriptFrame* frame = it.frame();
8470 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8471 Handle<Code> code(function->code());
8472 ScopeInfo<> sinfo(*code);
8473
8474 // Traverse the saved contexts chain to find the active context for the
8475 // selected frame.
8476 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008477 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008478 save = save->prev();
8479 }
8480 ASSERT(save != NULL);
8481 SaveContext savex;
8482 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008483
8484 // Create the (empty) function replacing the function on the stack frame for
8485 // the purpose of evaluating in the context created below. It is important
8486 // that this function does not describe any parameters and local variables
8487 // in the context. If it does then this will cause problems with the lookup
8488 // in Context::Lookup, where context slots for parameters and local variables
8489 // are looked at before the extension object.
8490 Handle<JSFunction> go_between =
8491 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
8492 go_between->set_context(function->context());
8493#ifdef DEBUG
8494 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
8495 ASSERT(go_between_sinfo.number_of_parameters() == 0);
8496 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
8497#endif
8498
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008499 // Materialize the content of the local scope into a JSObject.
8500 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008501
8502 // Allocate a new context for the debug evaluation and set the extension
8503 // object build.
8504 Handle<Context> context =
8505 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008506 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008507 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008508 Handle<Context> frame_context(Context::cast(frame->context()));
8509 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008510 context = CopyWithContextChain(frame_context, context);
8511
8512 // Wrap the evaluation statement in a new function compiled in the newly
8513 // created context. The function has one parameter which has to be called
8514 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00008515 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008516 // function(arguments,__source__) {return eval(__source__);}
8517 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00008518 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008519 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008520 Handle<String> function_source =
8521 Factory::NewStringFromAscii(Vector<const char>(source_str,
8522 source_str_length));
8523 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00008524 Compiler::CompileEval(function_source,
8525 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008526 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00008527 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008528 if (boilerplate.is_null()) return Failure::Exception();
8529 Handle<JSFunction> compiled_function =
8530 Factory::NewFunctionFromBoilerplate(boilerplate, context);
8531
8532 // Invoke the result of the compilation to get the evaluation function.
8533 bool has_pending_exception;
8534 Handle<Object> receiver(frame->receiver());
8535 Handle<Object> evaluation_function =
8536 Execution::Call(compiled_function, receiver, 0, NULL,
8537 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008538 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008539
8540 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
8541 function_context);
8542
8543 // Invoke the evaluation function and return the result.
8544 const int argc = 2;
8545 Object** argv[argc] = { arguments.location(),
8546 Handle<Object>::cast(source).location() };
8547 Handle<Object> result =
8548 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
8549 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008550 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008551
8552 // Skip the global proxy as it has no properties and always delegates to the
8553 // real global object.
8554 if (result->IsJSGlobalProxy()) {
8555 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
8556 }
8557
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008558 return *result;
8559}
8560
8561
8562static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
8563 HandleScope scope;
8564
8565 // Check the execution state and decode arguments frame and source to be
8566 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00008567 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008568 Object* check_result = Runtime_CheckExecutionState(args);
8569 if (check_result->IsFailure()) return check_result;
8570 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00008571 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
8572
8573 // Handle the processing of break.
8574 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008575
8576 // Enter the top context from before the debugger was invoked.
8577 SaveContext save;
8578 SaveContext* top = &save;
8579 while (top != NULL && *top->context() == *Debug::debug_context()) {
8580 top = top->prev();
8581 }
8582 if (top != NULL) {
8583 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008584 }
8585
8586 // Get the global context now set to the top context from before the
8587 // debugger was invoked.
8588 Handle<Context> context = Top::global_context();
8589
8590 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00008591 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008592 Handle<JSFunction>(Compiler::CompileEval(source,
8593 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00008594 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00008595 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008596 if (boilerplate.is_null()) return Failure::Exception();
8597 Handle<JSFunction> compiled_function =
8598 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
8599 context));
8600
8601 // Invoke the result of the compilation to get the evaluation function.
8602 bool has_pending_exception;
8603 Handle<Object> receiver = Top::global();
8604 Handle<Object> result =
8605 Execution::Call(compiled_function, receiver, 0, NULL,
8606 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008607 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008608 return *result;
8609}
8610
8611
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008612static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
8613 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00008614 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008615
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008616 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008617 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008618
8619 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008620 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00008621 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
8622 // Get the script wrapper in a local handle before calling GetScriptWrapper,
8623 // because using
8624 // instances->set(i, *GetScriptWrapper(script))
8625 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
8626 // already have deferenced the instances handle.
8627 Handle<JSValue> wrapper = GetScriptWrapper(script);
8628 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008629 }
8630
8631 // Return result as a JS array.
8632 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
8633 Handle<JSArray>::cast(result)->SetContent(*instances);
8634 return *result;
8635}
8636
8637
8638// Helper function used by Runtime_DebugReferencedBy below.
8639static int DebugReferencedBy(JSObject* target,
8640 Object* instance_filter, int max_references,
8641 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008642 JSFunction* arguments_function) {
8643 NoHandleAllocation ha;
8644 AssertNoAllocation no_alloc;
8645
8646 // Iterate the heap.
8647 int count = 0;
8648 JSObject* last = NULL;
8649 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008650 HeapObject* heap_obj = NULL;
8651 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008652 (max_references == 0 || count < max_references)) {
8653 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008654 if (heap_obj->IsJSObject()) {
8655 // Skip context extension objects and argument arrays as these are
8656 // checked in the context of functions using them.
8657 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00008658 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008659 obj->map()->constructor() == arguments_function) {
8660 continue;
8661 }
8662
8663 // Check if the JS object has a reference to the object looked for.
8664 if (obj->ReferencesObject(target)) {
8665 // Check instance filter if supplied. This is normally used to avoid
8666 // references from mirror objects (see Runtime_IsInPrototypeChain).
8667 if (!instance_filter->IsUndefined()) {
8668 Object* V = obj;
8669 while (true) {
8670 Object* prototype = V->GetPrototype();
8671 if (prototype->IsNull()) {
8672 break;
8673 }
8674 if (instance_filter == prototype) {
8675 obj = NULL; // Don't add this object.
8676 break;
8677 }
8678 V = prototype;
8679 }
8680 }
8681
8682 if (obj != NULL) {
8683 // Valid reference found add to instance array if supplied an update
8684 // count.
8685 if (instances != NULL && count < instances_size) {
8686 instances->set(count, obj);
8687 }
8688 last = obj;
8689 count++;
8690 }
8691 }
8692 }
8693 }
8694
8695 // Check for circular reference only. This can happen when the object is only
8696 // referenced from mirrors and has a circular reference in which case the
8697 // object is not really alive and would have been garbage collected if not
8698 // referenced from the mirror.
8699 if (count == 1 && last == target) {
8700 count = 0;
8701 }
8702
8703 // Return the number of referencing objects found.
8704 return count;
8705}
8706
8707
8708// Scan the heap for objects with direct references to an object
8709// args[0]: the object to find references to
8710// args[1]: constructor function for instances to exclude (Mirror)
8711// args[2]: the the maximum number of objects to return
8712static Object* Runtime_DebugReferencedBy(Arguments args) {
8713 ASSERT(args.length() == 3);
8714
8715 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008716 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008717
8718 // Check parameters.
8719 CONVERT_CHECKED(JSObject, target, args[0]);
8720 Object* instance_filter = args[1];
8721 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
8722 instance_filter->IsJSObject());
8723 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
8724 RUNTIME_ASSERT(max_references >= 0);
8725
8726 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008727 JSObject* arguments_boilerplate =
8728 Top::context()->global_context()->arguments_boilerplate();
8729 JSFunction* arguments_function =
8730 JSFunction::cast(arguments_boilerplate->map()->constructor());
8731
8732 // Get the number of referencing objects.
8733 int count;
8734 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00008735 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008736
8737 // Allocate an array to hold the result.
8738 Object* object = Heap::AllocateFixedArray(count);
8739 if (object->IsFailure()) return object;
8740 FixedArray* instances = FixedArray::cast(object);
8741
8742 // Fill the referencing objects.
8743 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00008744 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008745
8746 // Return result as JS array.
8747 Object* result =
8748 Heap::AllocateJSObject(
8749 Top::context()->global_context()->array_function());
8750 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
8751 return result;
8752}
8753
8754
8755// Helper function used by Runtime_DebugConstructedBy below.
8756static int DebugConstructedBy(JSFunction* constructor, int max_references,
8757 FixedArray* instances, int instances_size) {
8758 AssertNoAllocation no_alloc;
8759
8760 // Iterate the heap.
8761 int count = 0;
8762 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008763 HeapObject* heap_obj = NULL;
8764 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008765 (max_references == 0 || count < max_references)) {
8766 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008767 if (heap_obj->IsJSObject()) {
8768 JSObject* obj = JSObject::cast(heap_obj);
8769 if (obj->map()->constructor() == constructor) {
8770 // Valid reference found add to instance array if supplied an update
8771 // count.
8772 if (instances != NULL && count < instances_size) {
8773 instances->set(count, obj);
8774 }
8775 count++;
8776 }
8777 }
8778 }
8779
8780 // Return the number of referencing objects found.
8781 return count;
8782}
8783
8784
8785// Scan the heap for objects constructed by a specific function.
8786// args[0]: the constructor to find instances of
8787// args[1]: the the maximum number of objects to return
8788static Object* Runtime_DebugConstructedBy(Arguments args) {
8789 ASSERT(args.length() == 2);
8790
8791 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008792 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008793
8794 // Check parameters.
8795 CONVERT_CHECKED(JSFunction, constructor, args[0]);
8796 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
8797 RUNTIME_ASSERT(max_references >= 0);
8798
8799 // Get the number of referencing objects.
8800 int count;
8801 count = DebugConstructedBy(constructor, max_references, NULL, 0);
8802
8803 // Allocate an array to hold the result.
8804 Object* object = Heap::AllocateFixedArray(count);
8805 if (object->IsFailure()) return object;
8806 FixedArray* instances = FixedArray::cast(object);
8807
8808 // Fill the referencing objects.
8809 count = DebugConstructedBy(constructor, max_references, instances, count);
8810
8811 // Return result as JS array.
8812 Object* result =
8813 Heap::AllocateJSObject(
8814 Top::context()->global_context()->array_function());
8815 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
8816 return result;
8817}
8818
8819
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008820// Find the effective prototype object as returned by __proto__.
8821// args[0]: the object to find the prototype for.
8822static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008823 ASSERT(args.length() == 1);
8824
8825 CONVERT_CHECKED(JSObject, obj, args[0]);
8826
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008827 // Use the __proto__ accessor.
8828 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008829}
8830
8831
8832static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00008833 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008834 CPU::DebugBreak();
8835 return Heap::undefined_value();
8836}
8837
8838
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008839static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008840#ifdef DEBUG
8841 HandleScope scope;
8842 ASSERT(args.length() == 1);
8843 // Get the function and make sure it is compiled.
8844 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008845 Handle<SharedFunctionInfo> shared(func->shared());
8846 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008847 return Failure::Exception();
8848 }
8849 func->code()->PrintLn();
8850#endif // DEBUG
8851 return Heap::undefined_value();
8852}
ager@chromium.org9085a012009-05-11 19:22:57 +00008853
8854
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008855static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
8856#ifdef DEBUG
8857 HandleScope scope;
8858 ASSERT(args.length() == 1);
8859 // Get the function and make sure it is compiled.
8860 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008861 Handle<SharedFunctionInfo> shared(func->shared());
8862 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008863 return Failure::Exception();
8864 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008865 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008866#endif // DEBUG
8867 return Heap::undefined_value();
8868}
8869
8870
ager@chromium.org9085a012009-05-11 19:22:57 +00008871static Object* Runtime_FunctionGetInferredName(Arguments args) {
8872 NoHandleAllocation ha;
8873 ASSERT(args.length() == 1);
8874
8875 CONVERT_CHECKED(JSFunction, f, args[0]);
8876 return f->shared()->inferred_name();
8877}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008878
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008879
8880static int FindSharedFunctionInfosForScript(Script* script,
8881 FixedArray* buffer) {
8882 AssertNoAllocation no_allocations;
8883
8884 int counter = 0;
8885 int buffer_size = buffer->length();
8886 HeapIterator iterator;
8887 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
8888 ASSERT(obj != NULL);
8889 if (!obj->IsSharedFunctionInfo()) {
8890 continue;
8891 }
8892 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
8893 if (shared->script() != script) {
8894 continue;
8895 }
8896 if (counter < buffer_size) {
8897 buffer->set(counter, shared);
8898 }
8899 counter++;
8900 }
8901 return counter;
8902}
8903
8904// For a script finds all SharedFunctionInfo's in the heap that points
8905// to this script. Returns JSArray of SharedFunctionInfo wrapped
8906// in OpaqueReferences.
8907static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
8908 Arguments args) {
8909 ASSERT(args.length() == 1);
8910 HandleScope scope;
8911 CONVERT_CHECKED(JSValue, script_value, args[0]);
8912
8913 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
8914
8915 const int kBufferSize = 32;
8916
8917 Handle<FixedArray> array;
8918 array = Factory::NewFixedArray(kBufferSize);
8919 int number = FindSharedFunctionInfosForScript(*script, *array);
8920 if (number > kBufferSize) {
8921 array = Factory::NewFixedArray(number);
8922 FindSharedFunctionInfosForScript(*script, *array);
8923 }
8924
8925 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
8926 result->set_length(Smi::FromInt(number));
8927
8928 LiveEdit::WrapSharedFunctionInfos(result);
8929
8930 return *result;
8931}
8932
8933// For a script calculates compilation information about all its functions.
8934// The script source is explicitly specified by the second argument.
8935// The source of the actual script is not used, however it is important that
8936// all generated code keeps references to this particular instance of script.
8937// Returns a JSArray of compilation infos. The array is ordered so that
8938// each function with all its descendant is always stored in a continues range
8939// with the function itself going first. The root function is a script function.
8940static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
8941 ASSERT(args.length() == 2);
8942 HandleScope scope;
8943 CONVERT_CHECKED(JSValue, script, args[0]);
8944 CONVERT_ARG_CHECKED(String, source, 1);
8945 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
8946
8947 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
8948
8949 if (Top::has_pending_exception()) {
8950 return Failure::Exception();
8951 }
8952
8953 return result;
8954}
8955
8956// Changes the source of the script to a new_source and creates a new
8957// script representing the old version of the script source.
8958static Object* Runtime_LiveEditReplaceScript(Arguments args) {
8959 ASSERT(args.length() == 3);
8960 HandleScope scope;
8961 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
8962 CONVERT_ARG_CHECKED(String, new_source, 1);
8963 CONVERT_ARG_CHECKED(String, old_script_name, 2);
8964 Handle<Script> original_script =
8965 Handle<Script>(Script::cast(original_script_value->value()));
8966
8967 Handle<String> original_source(String::cast(original_script->source()));
8968
8969 original_script->set_source(*new_source);
8970 Handle<Script> old_script = Factory::NewScript(original_source);
8971 old_script->set_name(*old_script_name);
8972 old_script->set_line_offset(original_script->line_offset());
8973 old_script->set_column_offset(original_script->column_offset());
8974 old_script->set_data(original_script->data());
8975 old_script->set_type(original_script->type());
8976 old_script->set_context_data(original_script->context_data());
8977 old_script->set_compilation_type(original_script->compilation_type());
8978 old_script->set_eval_from_shared(original_script->eval_from_shared());
8979 old_script->set_eval_from_instructions_offset(
8980 original_script->eval_from_instructions_offset());
8981
8982
8983 Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
8984
8985 return *(GetScriptWrapper(old_script));
8986}
8987
8988// Replaces code of SharedFunctionInfo with a new one.
8989static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
8990 ASSERT(args.length() == 2);
8991 HandleScope scope;
8992 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
8993 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
8994
8995 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
8996
8997 return Heap::undefined_value();
8998}
8999
9000// Connects SharedFunctionInfo to another script.
9001static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
9002 ASSERT(args.length() == 2);
9003 HandleScope scope;
9004 CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
9005 CONVERT_ARG_CHECKED(JSValue, script_value, 1);
9006 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9007
9008 LiveEdit::RelinkFunctionToScript(shared_info_array, script);
9009
9010 return Heap::undefined_value();
9011}
9012
9013// Updates positions of a shared function info (first parameter) according
9014// to script source change. Text change is described in second parameter as
9015// array of groups of 3 numbers:
9016// (change_begin, change_end, change_end_new_position).
9017// Each group describes a change in text; groups are sorted by change_begin.
9018static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9019 ASSERT(args.length() == 2);
9020 HandleScope scope;
9021 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9022 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9023
9024 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
9025
9026 return Heap::undefined_value();
9027}
9028
9029
9030static LiveEdit::FunctionPatchabilityStatus FindFunctionCodeOnStacks(
9031 Handle<SharedFunctionInfo> shared) {
9032 // TODO(635): check all threads, not only the current one.
9033 for (StackFrameIterator it; !it.done(); it.Advance()) {
9034 StackFrame* frame = it.frame();
9035 if (frame->code() == shared->code()) {
9036 return LiveEdit::FUNCTION_BLOCKED_ON_STACK;
9037 }
9038 }
9039 return LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH;
9040}
9041
9042// For array of SharedFunctionInfo's (each wrapped in JSValue)
9043// checks that none of them have activations on stacks (of any thread).
9044// Returns array of the same length with corresponding results of
9045// LiveEdit::FunctionPatchabilityStatus type.
9046static Object* Runtime_LiveEditCheckStackActivations(Arguments args) {
9047 ASSERT(args.length() == 1);
9048 HandleScope scope;
9049 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9050
9051
9052 int len = Smi::cast(shared_array->length())->value();
9053 Handle<JSArray> result = Factory::NewJSArray(len);
9054
9055 for (int i = 0; i < len; i++) {
9056 JSValue* wrapper = JSValue::cast(shared_array->GetElement(i));
9057 Handle<SharedFunctionInfo> shared(
9058 SharedFunctionInfo::cast(wrapper->value()));
9059 LiveEdit::FunctionPatchabilityStatus check_res =
9060 FindFunctionCodeOnStacks(shared);
9061 SetElement(result, i, Handle<Smi>(Smi::FromInt(check_res)));
9062 }
9063
9064 return *result;
9065}
9066
9067
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009068// A testing entry. Returns statement position which is the closest to
9069// source_position.
9070static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9071 ASSERT(args.length() == 2);
9072 HandleScope scope;
9073 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9074 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9075
9076 Handle<Code> code(function->code());
9077
9078 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9079 int closest_pc = 0;
9080 int distance = kMaxInt;
9081 while (!it.done()) {
9082 int statement_position = static_cast<int>(it.rinfo()->data());
9083 // Check if this break point is closer that what was previously found.
9084 if (source_position <= statement_position &&
9085 statement_position - source_position < distance) {
9086 closest_pc = it.rinfo()->pc() - code->instruction_start();
9087 distance = statement_position - source_position;
9088 // Check whether we can't get any closer.
9089 if (distance == 0) break;
9090 }
9091 it.next();
9092 }
9093
9094 return Smi::FromInt(closest_pc);
9095}
9096
9097
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009098#endif // ENABLE_DEBUGGER_SUPPORT
9099
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009100#ifdef ENABLE_LOGGING_AND_PROFILING
9101
9102static Object* Runtime_ProfilerResume(Arguments args) {
9103 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009104 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009105
9106 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009107 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9108 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009109 return Heap::undefined_value();
9110}
9111
9112
9113static Object* Runtime_ProfilerPause(Arguments args) {
9114 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009115 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009116
9117 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009118 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9119 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009120 return Heap::undefined_value();
9121}
9122
9123#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009124
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009125// Finds the script object from the script data. NOTE: This operation uses
9126// heap traversal to find the function generated for the source position
9127// for the requested break point. For lazily compiled functions several heap
9128// traversals might be required rendering this operation as a rather slow
9129// operation. However for setting break points which is normally done through
9130// some kind of user interaction the performance is not crucial.
9131static Handle<Object> Runtime_GetScriptFromScriptName(
9132 Handle<String> script_name) {
9133 // Scan the heap for Script objects to find the script with the requested
9134 // script data.
9135 Handle<Script> script;
9136 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009137 HeapObject* obj = NULL;
9138 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009139 // If a script is found check if it has the script data requested.
9140 if (obj->IsScript()) {
9141 if (Script::cast(obj)->name()->IsString()) {
9142 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9143 script = Handle<Script>(Script::cast(obj));
9144 }
9145 }
9146 }
9147 }
9148
9149 // If no script with the requested script data is found return undefined.
9150 if (script.is_null()) return Factory::undefined_value();
9151
9152 // Return the script found.
9153 return GetScriptWrapper(script);
9154}
9155
9156
9157// Get the script object from script data. NOTE: Regarding performance
9158// see the NOTE for GetScriptFromScriptData.
9159// args[0]: script data for the script to find the source for
9160static Object* Runtime_GetScript(Arguments args) {
9161 HandleScope scope;
9162
9163 ASSERT(args.length() == 1);
9164
9165 CONVERT_CHECKED(String, script_name, args[0]);
9166
9167 // Find the requested script.
9168 Handle<Object> result =
9169 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
9170 return *result;
9171}
9172
9173
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009174// Determines whether the given stack frame should be displayed in
9175// a stack trace. The caller is the error constructor that asked
9176// for the stack trace to be collected. The first time a construct
9177// call to this function is encountered it is skipped. The seen_caller
9178// in/out parameter is used to remember if the caller has been seen
9179// yet.
9180static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
9181 bool* seen_caller) {
9182 // Only display JS frames.
9183 if (!raw_frame->is_java_script())
9184 return false;
9185 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
9186 Object* raw_fun = frame->function();
9187 // Not sure when this can happen but skip it just in case.
9188 if (!raw_fun->IsJSFunction())
9189 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009190 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009191 *seen_caller = true;
9192 return false;
9193 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009194 // Skip all frames until we've seen the caller. Also, skip the most
9195 // obvious builtin calls. Some builtin calls (such as Number.ADD
9196 // which is invoked using 'call') are very difficult to recognize
9197 // so we're leaving them in for now.
9198 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009199}
9200
9201
9202// Collect the raw data for a stack trace. Returns an array of three
9203// element segments each containing a receiver, function and native
9204// code offset.
9205static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009206 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009207 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009208 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
9209
9210 HandleScope scope;
9211
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009212 limit = Max(limit, 0); // Ensure that limit is not negative.
9213 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009214 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009215
9216 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009217 // If the caller parameter is a function we skip frames until we're
9218 // under it before starting to collect.
9219 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009220 int cursor = 0;
9221 int frames_seen = 0;
9222 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009223 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009224 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009225 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009226 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009227 Object* recv = frame->receiver();
9228 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009229 Address pc = frame->pc();
9230 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009231 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009232 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009233 if (cursor + 2 < elements->length()) {
9234 elements->set(cursor++, recv);
9235 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009236 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009237 } else {
9238 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009239 Handle<Object> recv_handle(recv);
9240 Handle<Object> fun_handle(fun);
9241 SetElement(result, cursor++, recv_handle);
9242 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009243 SetElement(result, cursor++, Handle<Smi>(offset));
9244 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009245 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009246 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009247 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009248
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009249 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009250 return *result;
9251}
9252
9253
ager@chromium.org3811b432009-10-28 14:53:37 +00009254// Returns V8 version as a string.
9255static Object* Runtime_GetV8Version(Arguments args) {
9256 ASSERT_EQ(args.length(), 0);
9257
9258 NoHandleAllocation ha;
9259
9260 const char* version_string = v8::V8::GetVersion();
9261
9262 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
9263}
9264
9265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009266static Object* Runtime_Abort(Arguments args) {
9267 ASSERT(args.length() == 2);
9268 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
9269 Smi::cast(args[1])->value());
9270 Top::PrintStack();
9271 OS::Abort();
9272 UNREACHABLE();
9273 return NULL;
9274}
9275
9276
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009277static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
9278 ASSERT(args.length() == 0);
9279 HandleScope::DeleteExtensions();
9280 return Heap::undefined_value();
9281}
9282
9283
kasper.lund44510672008-07-25 07:37:58 +00009284#ifdef DEBUG
9285// ListNatives is ONLY used by the fuzz-natives.js in debug mode
9286// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009287static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009288 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009289 HandleScope scope;
9290 Handle<JSArray> result = Factory::NewJSArray(0);
9291 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009292 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +00009293#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009294 { \
9295 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009296 Handle<String> name; \
9297 /* Inline runtime functions have an underscore in front of the name. */ \
9298 if (inline_runtime_functions) { \
9299 name = Factory::NewStringFromAscii( \
9300 Vector<const char>("_" #Name, StrLength("_" #Name))); \
9301 } else { \
9302 name = Factory::NewStringFromAscii( \
9303 Vector<const char>(#Name, StrLength(#Name))); \
9304 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009305 Handle<JSArray> pair = Factory::NewJSArray(0); \
9306 SetElement(pair, 0, name); \
9307 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
9308 SetElement(result, index++, pair); \
9309 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009310 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009311 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00009312 inline_runtime_functions = true;
9313 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009314#undef ADD_ENTRY
9315 return *result;
9316}
kasper.lund44510672008-07-25 07:37:58 +00009317#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009318
9319
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009320static Object* Runtime_Log(Arguments args) {
9321 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00009322 CONVERT_CHECKED(String, format, args[0]);
9323 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009324 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009325 Logger::LogRuntime(chars, elms);
9326 return Heap::undefined_value();
9327}
9328
9329
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009330static Object* Runtime_IS_VAR(Arguments args) {
9331 UNREACHABLE(); // implemented as macro in the parser
9332 return NULL;
9333}
9334
9335
9336// ----------------------------------------------------------------------------
9337// Implementation of Runtime
9338
ager@chromium.orga1645e22009-09-09 19:27:10 +00009339#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009340 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00009341 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009342
9343static Runtime::Function Runtime_functions[] = {
9344 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009345 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009346};
9347
9348#undef F
9349
9350
9351Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
9352 ASSERT(0 <= fid && fid < kNofFunctions);
9353 return &Runtime_functions[fid];
9354}
9355
9356
9357Runtime::Function* Runtime::FunctionForName(const char* name) {
9358 for (Function* f = Runtime_functions; f->name != NULL; f++) {
9359 if (strcmp(f->name, name) == 0) {
9360 return f;
9361 }
9362 }
9363 return NULL;
9364}
9365
9366
9367void Runtime::PerformGC(Object* result) {
9368 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009369 if (failure->IsRetryAfterGC()) {
9370 // Try to do a garbage collection; ignore it if it fails. The C
9371 // entry stub will throw an out-of-memory exception in that case.
9372 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
9373 } else {
9374 // Handle last resort GC and make sure to allow future allocations
9375 // to grow the heap without causing GCs (if possible).
9376 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009377 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00009378 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009379}
9380
9381
9382} } // namespace v8::internal