blob: 1442b98d6484f26e18b257001626e72e323e5892 [file] [log] [blame]
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000035#include "codegen.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036#include "compiler.h"
37#include "cpu.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000038#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include "debug.h"
40#include "execution.h"
41#include "jsregexp.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000042#include "liveedit.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000043#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044#include "platform.h"
45#include "runtime.h"
46#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000047#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000048#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000049#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
kasperl@chromium.org71affb52009-05-26 05:44:31 +000051namespace v8 {
52namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54
ager@chromium.org3e875802009-06-29 08:26:34 +000055#define RUNTIME_ASSERT(value) \
56 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057
58// Cast the given object to a value of the specified type and store
59// it in a variable with the given name. If the object is not of the
60// expected type call IllegalOperation and return.
61#define CONVERT_CHECKED(Type, name, obj) \
62 RUNTIME_ASSERT(obj->Is##Type()); \
63 Type* name = Type::cast(obj);
64
65#define CONVERT_ARG_CHECKED(Type, name, index) \
66 RUNTIME_ASSERT(args[index]->Is##Type()); \
67 Handle<Type> name = args.at<Type>(index);
68
kasper.lundbd3ec4e2008-07-09 11:06:54 +000069// Cast the given object to a boolean and store it in a variable with
70// the given name. If the object is not a boolean call IllegalOperation
71// and return.
72#define CONVERT_BOOLEAN_CHECKED(name, obj) \
73 RUNTIME_ASSERT(obj->IsBoolean()); \
74 bool name = (obj)->IsTrue();
75
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000076// Cast the given object to a Smi and store its value in an int variable
77// with the given name. If the object is not a Smi call IllegalOperation
78// and return.
79#define CONVERT_SMI_CHECKED(name, obj) \
80 RUNTIME_ASSERT(obj->IsSmi()); \
81 int name = Smi::cast(obj)->value();
82
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083// Cast the given object to a double and store it in a variable with
84// the given name. If the object is not a number (as opposed to
85// the number not-a-number) call IllegalOperation and return.
86#define CONVERT_DOUBLE_CHECKED(name, obj) \
87 RUNTIME_ASSERT(obj->IsNumber()); \
88 double name = (obj)->Number();
89
90// Call the specified converter on the object *comand store the result in
91// a variable of the specified type with the given name. If the
92// object is not a Number call IllegalOperation and return.
93#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
94 RUNTIME_ASSERT(obj->IsNumber()); \
95 type name = NumberTo##Type(obj);
96
97// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000098static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
102 StackLimitCheck check;
103 if (check.HasOverflowed()) return Top::StackOverflow();
104
105 Object* result = Heap::CopyJSObject(boilerplate);
106 if (result->IsFailure()) return result;
107 JSObject* copy = JSObject::cast(result);
108
109 // Deep copy local properties.
110 if (copy->HasFastProperties()) {
111 FixedArray* properties = copy->properties();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000112 for (int i = 0; i < properties->length(); i++) {
113 Object* value = properties->get(i);
114 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000115 JSObject* js_object = JSObject::cast(value);
116 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000117 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118 properties->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000119 }
120 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000125 JSObject* js_object = JSObject::cast(value);
126 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000128 copy->InObjectPropertyAtPut(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000138 String* key_string = String::cast(names->get(i));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000139 PropertyAttributes attributes =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140 copy->GetLocalPropertyAttribute(key_string);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000145 Object* value = copy->GetProperty(key_string, &attributes);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000148 JSObject* js_object = JSObject::cast(value);
149 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000150 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000151 result = copy->SetProperty(key_string, result, NONE);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000163 for (int i = 0; i < elements->length(); i++) {
164 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000165 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000166 JSObject* js_object = JSObject::cast(value);
167 result = DeepCopyBoilerplate(js_object);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000168 if (result->IsFailure()) return result;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000169 elements->set(i, result);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000170 }
171 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000172 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000173 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000174 case JSObject::DICTIONARY_ELEMENTS: {
175 NumberDictionary* element_dictionary = copy->element_dictionary();
176 int capacity = element_dictionary->Capacity();
177 for (int i = 0; i < capacity; i++) {
178 Object* k = element_dictionary->KeyAt(i);
179 if (element_dictionary->IsKey(k)) {
180 Object* value = element_dictionary->ValueAt(i);
181 if (value->IsJSObject()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000182 JSObject* js_object = JSObject::cast(value);
183 result = DeepCopyBoilerplate(js_object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 break;
190 }
191 default:
192 UNREACHABLE();
193 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000194 }
195 return copy;
196}
197
198
199static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
200 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
201 return DeepCopyBoilerplate(boilerplate);
202}
203
204
205static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000206 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000207 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208}
209
210
ager@chromium.org236ad962008-09-25 09:45:57 +0000211static Handle<Map> ComputeObjectLiteralMap(
212 Handle<Context> context,
213 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000214 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000215 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000216 if (FLAG_canonicalize_object_literal_maps) {
217 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000218 int number_of_symbol_keys = 0;
219 while ((number_of_symbol_keys < number_of_properties) &&
220 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
221 number_of_symbol_keys++;
222 }
223 // Based on the number of prefix symbols key we decide whether
224 // to use the map cache in the global context.
225 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000226 if ((number_of_symbol_keys == number_of_properties) &&
227 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000228 // Create the fixed array with the key.
229 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
230 for (int i = 0; i < number_of_symbol_keys; i++) {
231 keys->set(i, constant_properties->get(i*2));
232 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000233 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000234 return Factory::ObjectLiteralMapFromCache(context, keys);
235 }
236 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000237 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000238 return Factory::CopyMap(
239 Handle<Map>(context->object_function()->initial_map()),
240 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000241}
242
243
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000244static Handle<Object> CreateLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000248
249static Handle<Object> CreateObjectLiteralBoilerplate(
250 Handle<FixedArray> literals,
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000251 Handle<FixedArray> constant_properties,
252 bool should_have_fast_elements) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000268
269 // Normalize the elements of the boilerplate to save space if needed.
270 if (!should_have_fast_elements) NormalizeElements(boilerplate);
271
ager@chromium.org32912102009-01-16 10:38:43 +0000272 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000274 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000275 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000276 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 for (int index = 0; index < length; index +=2) {
278 Handle<Object> key(constant_properties->get(index+0));
279 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000280 if (value->IsFixedArray()) {
281 // The value contains the constant_properties of a
282 // simple object literal.
283 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
284 value = CreateLiteralBoilerplate(literals, array);
285 if (value.is_null()) return value;
286 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000287 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288 uint32_t element_index = 0;
289 if (key->IsSymbol()) {
290 // If key is a symbol it is not an array element.
291 Handle<String> name(String::cast(*key));
292 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000293 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294 } else if (Array::IndexFromObject(*key, &element_index)) {
295 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 // Non-uint32 number.
299 ASSERT(key->IsNumber());
300 double num = key->Number();
301 char arr[100];
302 Vector<char> buffer(arr, ARRAY_SIZE(arr));
303 const char* str = DoubleToCString(num, buffer);
304 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000305 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000307 // If setting the property on the boilerplate throws an
308 // exception, the exception is converted to an empty handle in
309 // the handle based operations. In that case, we need to
310 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 }
313 }
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316}
317
318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000319static Handle<Object> CreateArrayLiteralBoilerplate(
320 Handle<FixedArray> literals,
321 Handle<FixedArray> elements) {
322 // Create the JSArray.
323 Handle<JSFunction> constructor(
324 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
325 Handle<Object> object = Factory::NewJSObject(constructor);
326
327 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
328
329 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
330 for (int i = 0; i < content->length(); i++) {
331 if (content->get(i)->IsFixedArray()) {
332 // The value contains the constant_properties of a
333 // simple object literal.
334 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
335 Handle<Object> result =
336 CreateLiteralBoilerplate(literals, fa);
337 if (result.is_null()) return result;
338 content->set(i, *result);
339 }
340 }
341
342 // Set the elements.
343 Handle<JSArray>::cast(object)->SetContent(*content);
344 return object;
345}
346
347
348static Handle<Object> CreateLiteralBoilerplate(
349 Handle<FixedArray> literals,
350 Handle<FixedArray> array) {
351 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
352 switch (CompileTimeValue::GetType(array)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000353 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
354 return CreateObjectLiteralBoilerplate(literals, elements, true);
355 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
356 return CreateObjectLiteralBoilerplate(literals, elements, false);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000357 case CompileTimeValue::ARRAY_LITERAL:
358 return CreateArrayLiteralBoilerplate(literals, elements);
359 default:
360 UNREACHABLE();
361 return Handle<Object>::null();
362 }
363}
364
365
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000366static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000367 // Takes a FixedArray of elements containing the literal elements of
368 // the array literal and produces JSArray with those elements.
369 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000370 // which contains the context from which to get the Array function
371 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000372 HandleScope scope;
373 ASSERT(args.length() == 3);
374 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
375 CONVERT_SMI_CHECKED(literals_index, args[1]);
376 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000377
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000378 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
379 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 // Update the functions literal and return the boilerplate.
382 literals->set(literals_index, *object);
383 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384}
385
386
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000387static Object* Runtime_CreateObjectLiteral(Arguments args) {
388 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000389 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000390 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
391 CONVERT_SMI_CHECKED(literals_index, args[1]);
392 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000393 CONVERT_SMI_CHECKED(fast_elements, args[3]);
394 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000395
396 // Check if boilerplate exists. If not, create it first.
397 Handle<Object> boilerplate(literals->get(literals_index));
398 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000399 boilerplate = CreateObjectLiteralBoilerplate(literals,
400 constant_properties,
401 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000402 if (boilerplate.is_null()) return Failure::Exception();
403 // Update the functions literal and return the boilerplate.
404 literals->set(literals_index, *boilerplate);
405 }
406 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
407}
408
409
410static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
411 HandleScope scope;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000412 ASSERT(args.length() == 4);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000413 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
414 CONVERT_SMI_CHECKED(literals_index, args[1]);
415 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000416 CONVERT_SMI_CHECKED(fast_elements, args[3]);
417 bool should_have_fast_elements = fast_elements == 1;
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000418
419 // Check if boilerplate exists. If not, create it first.
420 Handle<Object> boilerplate(literals->get(literals_index));
421 if (*boilerplate == Heap::undefined_value()) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +0000422 boilerplate = CreateObjectLiteralBoilerplate(literals,
423 constant_properties,
424 should_have_fast_elements);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000425 if (boilerplate.is_null()) return Failure::Exception();
426 // Update the functions literal and return the boilerplate.
427 literals->set(literals_index, *boilerplate);
428 }
429 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
430}
431
432
433static Object* Runtime_CreateArrayLiteral(Arguments args) {
434 HandleScope scope;
435 ASSERT(args.length() == 3);
436 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
437 CONVERT_SMI_CHECKED(literals_index, args[1]);
438 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
439
440 // Check if boilerplate exists. If not, create it first.
441 Handle<Object> boilerplate(literals->get(literals_index));
442 if (*boilerplate == Heap::undefined_value()) {
443 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
444 if (boilerplate.is_null()) return Failure::Exception();
445 // Update the functions literal and return the boilerplate.
446 literals->set(literals_index, *boilerplate);
447 }
448 return DeepCopyBoilerplate(JSObject::cast(*boilerplate));
449}
450
451
452static Object* Runtime_CreateArrayLiteralShallow(Arguments args) {
453 HandleScope scope;
454 ASSERT(args.length() == 3);
455 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
456 CONVERT_SMI_CHECKED(literals_index, args[1]);
457 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
458
459 // Check if boilerplate exists. If not, create it first.
460 Handle<Object> boilerplate(literals->get(literals_index));
461 if (*boilerplate == Heap::undefined_value()) {
462 boilerplate = CreateArrayLiteralBoilerplate(literals, elements);
463 if (boilerplate.is_null()) return Failure::Exception();
464 // Update the functions literal and return the boilerplate.
465 literals->set(literals_index, *boilerplate);
466 }
467 return Heap::CopyJSObject(JSObject::cast(*boilerplate));
468}
469
470
ager@chromium.org32912102009-01-16 10:38:43 +0000471static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
472 ASSERT(args.length() == 2);
473 CONVERT_CHECKED(String, key, args[0]);
474 Object* value = args[1];
475 // Create a catch context extension object.
476 JSFunction* constructor =
477 Top::context()->global_context()->context_extension_function();
478 Object* object = Heap::AllocateJSObject(constructor);
479 if (object->IsFailure()) return object;
480 // Assign the exception value to the catch variable and make sure
481 // that the catch variable is DontDelete.
482 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
483 if (value->IsFailure()) return value;
484 return object;
485}
486
487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488static Object* Runtime_ClassOf(Arguments args) {
489 NoHandleAllocation ha;
490 ASSERT(args.length() == 1);
491 Object* obj = args[0];
492 if (!obj->IsJSObject()) return Heap::null_value();
493 return JSObject::cast(obj)->class_name();
494}
495
ager@chromium.org7c537e22008-10-16 08:43:32 +0000496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497static Object* Runtime_IsInPrototypeChain(Arguments args) {
498 NoHandleAllocation ha;
499 ASSERT(args.length() == 2);
500 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
501 Object* O = args[0];
502 Object* V = args[1];
503 while (true) {
504 Object* prototype = V->GetPrototype();
505 if (prototype->IsNull()) return Heap::false_value();
506 if (O == prototype) return Heap::true_value();
507 V = prototype;
508 }
509}
510
511
ager@chromium.org9085a012009-05-11 19:22:57 +0000512// Inserts an object as the hidden prototype of another object.
513static Object* Runtime_SetHiddenPrototype(Arguments args) {
514 NoHandleAllocation ha;
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(JSObject, jsobject, args[0]);
517 CONVERT_CHECKED(JSObject, proto, args[1]);
518
519 // Sanity checks. The old prototype (that we are replacing) could
520 // theoretically be null, but if it is not null then check that we
521 // didn't already install a hidden prototype here.
522 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
523 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
524 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
525
526 // Allocate up front before we start altering state in case we get a GC.
527 Object* map_or_failure = proto->map()->CopyDropTransitions();
528 if (map_or_failure->IsFailure()) return map_or_failure;
529 Map* new_proto_map = Map::cast(map_or_failure);
530
531 map_or_failure = jsobject->map()->CopyDropTransitions();
532 if (map_or_failure->IsFailure()) return map_or_failure;
533 Map* new_map = Map::cast(map_or_failure);
534
535 // Set proto's prototype to be the old prototype of the object.
536 new_proto_map->set_prototype(jsobject->GetPrototype());
537 proto->set_map(new_proto_map);
538 new_proto_map->set_is_hidden_prototype();
539
540 // Set the object's prototype to proto.
541 new_map->set_prototype(proto);
542 jsobject->set_map(new_map);
543
544 return Heap::undefined_value();
545}
546
547
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000548static Object* Runtime_IsConstructCall(Arguments args) {
549 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000550 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000551 JavaScriptFrameIterator it;
552 return Heap::ToBoolean(it.frame()->IsConstructor());
553}
554
555
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000556// Recursively traverses hidden prototypes if property is not found
557static void GetOwnPropertyImplementation(JSObject* obj,
558 String* name,
559 LookupResult* result) {
560 obj->LocalLookupRealNamedProperty(name, result);
561
562 if (!result->IsProperty()) {
563 Object* proto = obj->GetPrototype();
564 if (proto->IsJSObject() &&
565 JSObject::cast(proto)->map()->is_hidden_prototype())
566 GetOwnPropertyImplementation(JSObject::cast(proto),
567 name, result);
568 }
569}
570
571
572// Returns an array with the property description:
573// if args[1] is not a property on args[0]
574// returns undefined
575// if args[1] is a data property on args[0]
576// [false, value, Writeable, Enumerable, Configurable]
577// if args[1] is an accessor on args[0]
578// [true, GetFunction, SetFunction, Enumerable, Configurable]
579static Object* Runtime_GetOwnProperty(Arguments args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000580 ASSERT(args.length() == 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000581 HandleScope scope;
582 Handle<FixedArray> elms = Factory::NewFixedArray(5);
583 Handle<JSArray> desc = Factory::NewJSArrayWithElements(elms);
584 LookupResult result;
585 CONVERT_CHECKED(JSObject, obj, args[0]);
586 CONVERT_CHECKED(String, name, args[1]);
587
588 // Use recursive implementation to also traverse hidden prototypes
589 GetOwnPropertyImplementation(obj, name, &result);
590
591 if (!result.IsProperty())
592 return Heap::undefined_value();
593
594 if (result.type() == CALLBACKS) {
595 Object* structure = result.GetCallbackObject();
ager@chromium.org5c838252010-02-19 08:53:10 +0000596 if (structure->IsProxy() || structure->IsAccessorInfo()) {
597 // Property that is internally implemented as a callback or
598 // an API defined callback.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000599 Object* value = obj->GetPropertyWithCallback(
600 obj, structure, name, result.holder());
601 elms->set(0, Heap::false_value());
602 elms->set(1, value);
603 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
604 } else if (structure->IsFixedArray()) {
605 // __defineGetter__/__defineSetter__ callback.
606 elms->set(0, Heap::true_value());
607 elms->set(1, FixedArray::cast(structure)->get(0));
608 elms->set(2, FixedArray::cast(structure)->get(1));
609 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000610 return Heap::undefined_value();
611 }
612 } else {
613 elms->set(0, Heap::false_value());
614 elms->set(1, result.GetLazyValue());
615 elms->set(2, Heap::ToBoolean(!result.IsReadOnly()));
616 }
617
618 elms->set(3, Heap::ToBoolean(!result.IsDontEnum()));
ager@chromium.org5c838252010-02-19 08:53:10 +0000619 elms->set(4, Heap::ToBoolean(!result.IsDontDelete()));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000620 return *desc;
621}
622
623
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000624static Object* Runtime_IsExtensible(Arguments args) {
625 ASSERT(args.length() == 1);
626 CONVERT_CHECKED(JSObject, obj, args[0]);
627 return obj->map()->is_extensible() ? Heap::true_value()
628 : Heap::false_value();
629}
630
631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000632static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000633 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000634 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000635 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
636 CONVERT_ARG_CHECKED(String, pattern, 1);
637 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000638 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
639 if (result.is_null()) return Failure::Exception();
640 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641}
642
643
644static Object* Runtime_CreateApiFunction(Arguments args) {
645 HandleScope scope;
646 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000647 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000648 return *Factory::CreateApiFunction(data);
649}
650
651
652static Object* Runtime_IsTemplate(Arguments args) {
653 ASSERT(args.length() == 1);
654 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000655 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656 return Heap::ToBoolean(result);
657}
658
659
660static Object* Runtime_GetTemplateField(Arguments args) {
661 ASSERT(args.length() == 2);
662 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000663 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000664 int index = field->value();
665 int offset = index * kPointerSize + HeapObject::kHeaderSize;
666 InstanceType type = templ->map()->instance_type();
667 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
668 type == OBJECT_TEMPLATE_INFO_TYPE);
669 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000670 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000671 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
672 } else {
673 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
674 }
675 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676}
677
678
ager@chromium.org870a0b62008-11-04 11:43:05 +0000679static Object* Runtime_DisableAccessChecks(Arguments args) {
680 ASSERT(args.length() == 1);
681 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000682 Map* old_map = object->map();
683 bool needs_access_checks = old_map->is_access_check_needed();
684 if (needs_access_checks) {
685 // Copy map so it won't interfere constructor's initial map.
686 Object* new_map = old_map->CopyDropTransitions();
687 if (new_map->IsFailure()) return new_map;
688
689 Map::cast(new_map)->set_is_access_check_needed(false);
690 object->set_map(Map::cast(new_map));
691 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000692 return needs_access_checks ? Heap::true_value() : Heap::false_value();
693}
694
695
696static Object* Runtime_EnableAccessChecks(Arguments args) {
697 ASSERT(args.length() == 1);
698 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000699 Map* old_map = object->map();
700 if (!old_map->is_access_check_needed()) {
701 // Copy map so it won't interfere constructor's initial map.
702 Object* new_map = old_map->CopyDropTransitions();
703 if (new_map->IsFailure()) return new_map;
704
705 Map::cast(new_map)->set_is_access_check_needed(true);
706 object->set_map(Map::cast(new_map));
707 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000708 return Heap::undefined_value();
709}
710
711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000712static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
713 HandleScope scope;
714 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
715 Handle<Object> args[2] = { type_handle, name };
716 Handle<Object> error =
717 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
718 return Top::Throw(*error);
719}
720
721
722static Object* Runtime_DeclareGlobals(Arguments args) {
723 HandleScope scope;
724 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
725
ager@chromium.org3811b432009-10-28 14:53:37 +0000726 Handle<Context> context = args.at<Context>(0);
727 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 bool is_eval = Smi::cast(args[2])->value() == 1;
729
730 // Compute the property attributes. According to ECMA-262, section
731 // 13, page 71, the property must be read-only and
732 // non-deletable. However, neither SpiderMonkey nor KJS creates the
733 // property as read-only, so we don't either.
734 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Traverse the name/value pairs and set the properties.
737 int length = pairs->length();
738 for (int i = 0; i < length; i += 2) {
739 HandleScope scope;
740 Handle<String> name(String::cast(pairs->get(i)));
741 Handle<Object> value(pairs->get(i + 1));
742
743 // We have to declare a global const property. To capture we only
744 // assign to it when evaluating the assignment for "const x =
745 // <expr>" the initial value is the hole.
746 bool is_const_property = value->IsTheHole();
747
748 if (value->IsUndefined() || is_const_property) {
749 // Lookup the property in the global object, and don't set the
750 // value of the variable if the property is already there.
751 LookupResult lookup;
752 global->Lookup(*name, &lookup);
753 if (lookup.IsProperty()) {
754 // Determine if the property is local by comparing the holder
755 // against the global object. The information will be used to
756 // avoid throwing re-declaration errors when declaring
757 // variables or constants that exist in the prototype chain.
758 bool is_local = (*global == lookup.holder());
759 // Get the property attributes and determine if the property is
760 // read-only.
761 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
762 bool is_read_only = (attributes & READ_ONLY) != 0;
763 if (lookup.type() == INTERCEPTOR) {
764 // If the interceptor says the property is there, we
765 // just return undefined without overwriting the property.
766 // Otherwise, we continue to setting the property.
767 if (attributes != ABSENT) {
768 // Check if the existing property conflicts with regards to const.
769 if (is_local && (is_read_only || is_const_property)) {
770 const char* type = (is_read_only) ? "const" : "var";
771 return ThrowRedeclarationError(type, name);
772 };
773 // The property already exists without conflicting: Go to
774 // the next declaration.
775 continue;
776 }
777 // Fall-through and introduce the absent property by using
778 // SetProperty.
779 } else {
780 if (is_local && (is_read_only || is_const_property)) {
781 const char* type = (is_read_only) ? "const" : "var";
782 return ThrowRedeclarationError(type, name);
783 }
784 // The property already exists without conflicting: Go to
785 // the next declaration.
786 continue;
787 }
788 }
789 } else {
790 // Copy the function and update its context. Use it as value.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000791 Handle<SharedFunctionInfo> shared =
792 Handle<SharedFunctionInfo>::cast(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 Handle<JSFunction> function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000794 Factory::NewFunctionFromSharedFunctionInfo(shared, context, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000795 value = function;
796 }
797
798 LookupResult lookup;
799 global->LocalLookup(*name, &lookup);
800
801 PropertyAttributes attributes = is_const_property
802 ? static_cast<PropertyAttributes>(base | READ_ONLY)
803 : base;
804
805 if (lookup.IsProperty()) {
806 // There's a local property that we need to overwrite because
807 // we're either declaring a function or there's an interceptor
808 // that claims the property is absent.
809
810 // Check for conflicting re-declarations. We cannot have
811 // conflicting types in case of intercepted properties because
812 // they are absent.
813 if (lookup.type() != INTERCEPTOR &&
814 (lookup.IsReadOnly() || is_const_property)) {
815 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
816 return ThrowRedeclarationError(type, name);
817 }
818 SetProperty(global, name, value, attributes);
819 } else {
820 // If a property with this name does not already exist on the
821 // global object add the property locally. We take special
822 // precautions to always add it as a local property even in case
823 // of callbacks in the prototype chain (this rules out using
824 // SetProperty). Also, we must use the handle-based version to
825 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000826 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 }
828 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 return Heap::undefined_value();
831}
832
833
834static Object* Runtime_DeclareContextSlot(Arguments args) {
835 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000836 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837
ager@chromium.org7c537e22008-10-16 08:43:32 +0000838 CONVERT_ARG_CHECKED(Context, context, 0);
839 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000841 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000843 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000844
845 // Declarations are always done in the function context.
846 context = Handle<Context>(context->fcontext());
847
848 int index;
849 PropertyAttributes attributes;
850 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000851 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852 context->Lookup(name, flags, &index, &attributes);
853
854 if (attributes != ABSENT) {
855 // The name was declared before; check for conflicting
856 // re-declarations: This is similar to the code in parser.cc in
857 // the AstBuildingParser::Declare function.
858 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
859 // Functions are not read-only.
860 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
861 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
862 return ThrowRedeclarationError(type, name);
863 }
864
865 // Initialize it if necessary.
866 if (*initial_value != NULL) {
867 if (index >= 0) {
868 // The variable or constant context slot should always be in
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000869 // the function context or the arguments object.
870 if (holder->IsContext()) {
871 ASSERT(holder.is_identical_to(context));
872 if (((attributes & READ_ONLY) == 0) ||
873 context->get(index)->IsTheHole()) {
874 context->set(index, *initial_value);
875 }
876 } else {
877 Handle<JSObject>::cast(holder)->SetElement(index, *initial_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 }
879 } else {
880 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000881 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000882 SetProperty(context_ext, name, initial_value, mode);
883 }
884 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000886 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000887 // The property is not in the function context. It needs to be
888 // "declared" in the function context's extension context, or in the
889 // global context.
890 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000891 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000892 // The function context's extension context exists - use it.
893 context_ext = Handle<JSObject>(context->extension());
894 } else {
895 // The function context's extension context does not exists - allocate
896 // it.
897 context_ext = Factory::NewJSObject(Top::context_extension_function());
898 // And store it in the extension slot.
899 context->set_extension(*context_ext);
900 }
901 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902
ager@chromium.org7c537e22008-10-16 08:43:32 +0000903 // Declare the property by setting it to the initial value if provided,
904 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
905 // constant declarations).
906 ASSERT(!context_ext->HasLocalProperty(*name));
907 Handle<Object> value(Heap::undefined_value());
908 if (*initial_value != NULL) value = initial_value;
909 SetProperty(context_ext, name, value, mode);
910 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
911 }
912
913 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000914}
915
916
917static Object* Runtime_InitializeVarGlobal(Arguments args) {
918 NoHandleAllocation nha;
919
920 // Determine if we need to assign to the variable if it already
921 // exists (based on the number of arguments).
922 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
923 bool assign = args.length() == 2;
924
925 CONVERT_ARG_CHECKED(String, name, 0);
926 GlobalObject* global = Top::context()->global();
927
928 // According to ECMA-262, section 12.2, page 62, the property must
929 // not be deletable.
930 PropertyAttributes attributes = DONT_DELETE;
931
932 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000933 // there, there is a property with this name in the prototype chain.
934 // We follow Safari and Firefox behavior and only set the property
935 // locally if there is an explicit initialization value that we have
936 // to assign to the property. When adding the property we take
937 // special precautions to always add it as a local property even in
938 // case of callbacks in the prototype chain (this rules out using
939 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
940 // this.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000941 // Note that objects can have hidden prototypes, so we need to traverse
942 // the whole chain of hidden prototypes to do a 'local' lookup.
943 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000945 while (true) {
946 real_holder->LocalLookup(*name, &lookup);
947 if (lookup.IsProperty()) {
948 // Determine if this is a redeclaration of something read-only.
949 if (lookup.IsReadOnly()) {
950 // If we found readonly property on one of hidden prototypes,
951 // just shadow it.
952 if (real_holder != Top::context()->global()) break;
953 return ThrowRedeclarationError("const", name);
954 }
955
956 // Determine if this is a redeclaration of an intercepted read-only
957 // property and figure out if the property exists at all.
958 bool found = true;
959 PropertyType type = lookup.type();
960 if (type == INTERCEPTOR) {
961 HandleScope handle_scope;
962 Handle<JSObject> holder(real_holder);
963 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
964 real_holder = *holder;
965 if (intercepted == ABSENT) {
966 // The interceptor claims the property isn't there. We need to
967 // make sure to introduce it.
968 found = false;
969 } else if ((intercepted & READ_ONLY) != 0) {
970 // The property is present, but read-only. Since we're trying to
971 // overwrite it with a variable declaration we must throw a
972 // re-declaration error. However if we found readonly property
973 // on one of hidden prototypes, just shadow it.
974 if (real_holder != Top::context()->global()) break;
975 return ThrowRedeclarationError("const", name);
976 }
977 }
978
979 if (found && !assign) {
980 // The global property is there and we're not assigning any value
981 // to it. Just return.
982 return Heap::undefined_value();
983 }
984
985 // Assign the value (or undefined) to the property.
986 Object* value = (assign) ? args[1] : Heap::undefined_value();
987 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000988 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000989
990 Object* proto = real_holder->GetPrototype();
991 if (!proto->IsJSObject())
992 break;
993
994 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
995 break;
996
997 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998 }
999
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001000 global = Top::context()->global();
1001 if (assign) {
1002 return global->IgnoreAttributesAndSetLocalProperty(*name,
1003 args[1],
1004 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001006 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007}
1008
1009
1010static Object* Runtime_InitializeConstGlobal(Arguments args) {
1011 // All constants are declared with an initial value. The name
1012 // of the constant is the first argument and the initial value
1013 // is the second.
1014 RUNTIME_ASSERT(args.length() == 2);
1015 CONVERT_ARG_CHECKED(String, name, 0);
1016 Handle<Object> value = args.at<Object>(1);
1017
1018 // Get the current global object from top.
1019 GlobalObject* global = Top::context()->global();
1020
1021 // According to ECMA-262, section 12.2, page 62, the property must
1022 // not be deletable. Since it's a const, it must be READ_ONLY too.
1023 PropertyAttributes attributes =
1024 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
1025
1026 // Lookup the property locally in the global object. If it isn't
1027 // there, we add the property and take special precautions to always
1028 // add it as a local property even in case of callbacks in the
1029 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001030 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031 LookupResult lookup;
1032 global->LocalLookup(*name, &lookup);
1033 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001034 return global->IgnoreAttributesAndSetLocalProperty(*name,
1035 *value,
1036 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001037 }
1038
1039 // Determine if this is a redeclaration of something not
1040 // read-only. In case the result is hidden behind an interceptor we
1041 // need to ask it for the property attributes.
1042 if (!lookup.IsReadOnly()) {
1043 if (lookup.type() != INTERCEPTOR) {
1044 return ThrowRedeclarationError("var", name);
1045 }
1046
1047 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
1048
1049 // Throw re-declaration error if the intercepted property is present
1050 // but not read-only.
1051 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
1052 return ThrowRedeclarationError("var", name);
1053 }
1054
1055 // Restore global object from context (in case of GC) and continue
1056 // with setting the value because the property is either absent or
1057 // read-only. We also have to do redo the lookup.
1058 global = Top::context()->global();
1059
1060 // BUG 1213579: Handle the case where we have to set a read-only
1061 // property through an interceptor and only do it if it's
1062 // uninitialized, e.g. the hole. Nirk...
1063 global->SetProperty(*name, *value, attributes);
1064 return *value;
1065 }
1066
1067 // Set the value, but only we're assigning the initial value to a
1068 // constant. For now, we determine this by checking if the
1069 // current value is the hole.
1070 PropertyType type = lookup.type();
1071 if (type == FIELD) {
1072 FixedArray* properties = global->properties();
1073 int index = lookup.GetFieldIndex();
1074 if (properties->get(index)->IsTheHole()) {
1075 properties->set(index, *value);
1076 }
1077 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001078 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
1079 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
1081 } else {
1082 // Ignore re-initialization of constants that have already been
1083 // assigned a function value.
1084 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
1085 }
1086
1087 // Use the set value as the result of the operation.
1088 return *value;
1089}
1090
1091
1092static Object* Runtime_InitializeConstContextSlot(Arguments args) {
1093 HandleScope scope;
1094 ASSERT(args.length() == 3);
1095
1096 Handle<Object> value(args[0]);
1097 ASSERT(!value->IsTheHole());
1098 CONVERT_ARG_CHECKED(Context, context, 1);
1099 Handle<String> name(String::cast(args[2]));
1100
1101 // Initializations are always done in the function context.
1102 context = Handle<Context>(context->fcontext());
1103
1104 int index;
1105 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001106 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001107 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001108 context->Lookup(name, flags, &index, &attributes);
1109
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001110 // In most situations, the property introduced by the const
1111 // declaration should be present in the context extension object.
1112 // However, because declaration and initialization are separate, the
1113 // property might have been deleted (if it was introduced by eval)
1114 // before we reach the initialization point.
1115 //
1116 // Example:
1117 //
1118 // function f() { eval("delete x; const x;"); }
1119 //
1120 // In that case, the initialization behaves like a normal assignment
1121 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001123 // Property was found in a context.
1124 if (holder->IsContext()) {
1125 // The holder cannot be the function context. If it is, there
1126 // should have been a const redeclaration error when declaring
1127 // the const property.
1128 ASSERT(!holder.is_identical_to(context));
1129 if ((attributes & READ_ONLY) == 0) {
1130 Handle<Context>::cast(holder)->set(index, *value);
1131 }
1132 } else {
1133 // The holder is an arguments object.
1134 ASSERT((attributes & READ_ONLY) == 0);
1135 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137 return *value;
1138 }
1139
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001140 // The property could not be found, we introduce it in the global
1141 // context.
1142 if (attributes == ABSENT) {
1143 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
1144 SetProperty(global, name, value, NONE);
1145 return *value;
1146 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001147
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001148 // The property was present in a context extension object.
1149 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001150
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001151 if (*context_ext == context->extension()) {
1152 // This is the property that was introduced by the const
1153 // declaration. Set it if it hasn't been set before. NOTE: We
1154 // cannot use GetProperty() to get the current value as it
1155 // 'unholes' the value.
1156 LookupResult lookup;
1157 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1158 ASSERT(lookup.IsProperty()); // the property was declared
1159 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1160
1161 PropertyType type = lookup.type();
1162 if (type == FIELD) {
1163 FixedArray* properties = context_ext->properties();
1164 int index = lookup.GetFieldIndex();
1165 if (properties->get(index)->IsTheHole()) {
1166 properties->set(index, *value);
1167 }
1168 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001169 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1170 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001171 }
1172 } else {
1173 // We should not reach here. Any real, named property should be
1174 // either a field or a dictionary slot.
1175 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176 }
1177 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001178 // The property was found in a different context extension object.
1179 // Set it if it is not a read-only property.
1180 if ((attributes & READ_ONLY) == 0) {
1181 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1182 // Setting a property might throw an exception. Exceptions
1183 // are converted to empty handles in handle operations. We
1184 // need to convert back to exceptions here.
1185 if (set.is_null()) {
1186 ASSERT(Top::has_pending_exception());
1187 return Failure::Exception();
1188 }
1189 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 return *value;
1193}
1194
1195
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001196static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1197 Arguments args) {
1198 HandleScope scope;
1199 ASSERT(args.length() == 2);
1200 CONVERT_ARG_CHECKED(JSObject, object, 0);
1201 CONVERT_SMI_CHECKED(properties, args[1]);
1202 if (object->HasFastProperties()) {
1203 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1204 }
1205 return *object;
1206}
1207
1208
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209static Object* Runtime_RegExpExec(Arguments args) {
1210 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001211 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001212 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1213 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001214 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001215 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001216 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001217 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001218 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001219 RUNTIME_ASSERT(index >= 0);
1220 RUNTIME_ASSERT(index <= subject->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001221 Counters::regexp_entry_runtime.Increment();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001222 Handle<Object> result = RegExpImpl::Exec(regexp,
1223 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001224 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001225 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001226 if (result.is_null()) return Failure::Exception();
1227 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228}
1229
1230
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001231static Object* Runtime_RegExpConstructResult(Arguments args) {
1232 ASSERT(args.length() == 3);
1233 CONVERT_SMI_CHECKED(elements_count, args[0]);
1234 if (elements_count > JSArray::kMaxFastElementsLength) {
1235 return Top::ThrowIllegalOperation();
1236 }
1237 Object* new_object = Heap::AllocateFixedArrayWithHoles(elements_count);
1238 if (new_object->IsFailure()) return new_object;
1239 FixedArray* elements = FixedArray::cast(new_object);
1240 new_object = Heap::AllocateRaw(JSRegExpResult::kSize,
1241 NEW_SPACE,
1242 OLD_POINTER_SPACE);
1243 if (new_object->IsFailure()) return new_object;
1244 {
1245 AssertNoAllocation no_gc;
1246 HandleScope scope;
1247 reinterpret_cast<HeapObject*>(new_object)->
1248 set_map(Top::global_context()->regexp_result_map());
1249 }
1250 JSArray* array = JSArray::cast(new_object);
1251 array->set_properties(Heap::empty_fixed_array());
1252 array->set_elements(elements);
1253 array->set_length(Smi::FromInt(elements_count));
1254 // Write in-object properties after the length of the array.
1255 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
1256 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
1257 return array;
1258}
1259
1260
lrn@chromium.org25156de2010-04-06 13:10:27 +00001261static Object* Runtime_RegExpInitializeObject(Arguments args) {
1262 AssertNoAllocation no_alloc;
1263 ASSERT(args.length() == 5);
1264 CONVERT_CHECKED(JSRegExp, regexp, args[0]);
1265 CONVERT_CHECKED(String, source, args[1]);
1266
1267 Object* global = args[2];
1268 if (!global->IsTrue()) global = Heap::false_value();
1269
1270 Object* ignoreCase = args[3];
1271 if (!ignoreCase->IsTrue()) ignoreCase = Heap::false_value();
1272
1273 Object* multiline = args[4];
1274 if (!multiline->IsTrue()) multiline = Heap::false_value();
1275
1276 Map* map = regexp->map();
1277 Object* constructor = map->constructor();
1278 if (constructor->IsJSFunction() &&
1279 JSFunction::cast(constructor)->initial_map() == map) {
1280 // If we still have the original map, set in-object properties directly.
1281 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
1282 // TODO(lrn): Consider skipping write barrier on booleans as well.
1283 // Both true and false should be in oldspace at all times.
1284 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, global);
1285 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, ignoreCase);
1286 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, multiline);
1287 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
1288 Smi::FromInt(0),
1289 SKIP_WRITE_BARRIER);
1290 return regexp;
1291 }
1292
1293 // Map has changed, so use generic, but slower, method.
1294 PropertyAttributes final =
1295 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
1296 PropertyAttributes writable =
1297 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
1298 regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
1299 source,
1300 final);
1301 regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
1302 global,
1303 final);
1304 regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
1305 ignoreCase,
1306 final);
1307 regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
1308 multiline,
1309 final);
1310 regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
1311 Smi::FromInt(0),
1312 writable);
1313 return regexp;
1314}
1315
1316
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001317static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
1318 HandleScope scope;
1319 ASSERT(args.length() == 1);
1320 CONVERT_ARG_CHECKED(JSArray, prototype, 0);
1321 // This is necessary to enable fast checks for absence of elements
1322 // on Array.prototype and below.
1323 prototype->set_elements(Heap::empty_fixed_array());
1324 return Smi::FromInt(0);
1325}
1326
1327
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001328static void SetCustomCallGenerator(Handle<JSFunction> function,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001329 ExternalReference* generator) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001330 if (function->shared()->function_data()->IsUndefined()) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001331 function->shared()->set_function_data(*FromCData(generator->address()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001332 }
1333}
1334
1335
1336static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
1337 const char* name,
1338 Builtins::Name builtin_name,
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001339 ExternalReference* generator = NULL) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001340 Handle<String> key = Factory::LookupAsciiSymbol(name);
1341 Handle<Code> code(Builtins::builtin(builtin_name));
1342 Handle<JSFunction> optimized = Factory::NewFunction(key,
1343 JS_OBJECT_TYPE,
1344 JSObject::kHeaderSize,
1345 code,
1346 false);
1347 optimized->shared()->DontAdaptArguments();
1348 if (generator != NULL) {
1349 SetCustomCallGenerator(optimized, generator);
1350 }
1351 SetProperty(holder, key, optimized, NONE);
1352 return optimized;
1353}
1354
1355
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001356Object* CompileArrayPushCall(CallStubCompiler* compiler,
1357 Object* object,
1358 JSObject* holder,
1359 JSFunction* function,
1360 String* name,
1361 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001362 return compiler->CompileArrayPushCall(object, holder, function, name, check);
1363}
1364
1365
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001366Object* CompileArrayPopCall(CallStubCompiler* compiler,
1367 Object* object,
1368 JSObject* holder,
1369 JSFunction* function,
1370 String* name,
1371 StubCompiler::CheckType check) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001372 return compiler->CompileArrayPopCall(object, holder, function, name, check);
1373}
1374
1375
1376static Object* Runtime_SpecialArrayFunctions(Arguments args) {
1377 HandleScope scope;
1378 ASSERT(args.length() == 1);
1379 CONVERT_ARG_CHECKED(JSObject, holder, 0);
1380
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001381 ExternalReference pop = ExternalReference::compile_array_pop_call();
1382 ExternalReference push = ExternalReference::compile_array_push_call();
1383
1384 InstallBuiltin(holder, "pop", Builtins::ArrayPop, &pop);
1385 InstallBuiltin(holder, "push", Builtins::ArrayPush, &push);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001386 InstallBuiltin(holder, "shift", Builtins::ArrayShift);
1387 InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
1388 InstallBuiltin(holder, "slice", Builtins::ArraySlice);
1389 InstallBuiltin(holder, "splice", Builtins::ArraySplice);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001390 InstallBuiltin(holder, "concat", Builtins::ArrayConcat);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001391
1392 return *holder;
1393}
1394
1395
ager@chromium.org357bf652010-04-12 11:30:10 +00001396static Object* Runtime_GetGlobalReceiver(Arguments args) {
1397 // Returns a real global receiver, not one of builtins object.
1398 Context* global_context = Top::context()->global()->global_context();
1399 return global_context->global()->global_receiver();
1400}
1401
1402
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001403static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1404 HandleScope scope;
1405 ASSERT(args.length() == 4);
1406 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1407 int index = Smi::cast(args[1])->value();
1408 Handle<String> pattern = args.at<String>(2);
1409 Handle<String> flags = args.at<String>(3);
1410
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001411 // Get the RegExp function from the context in the literals array.
1412 // This is the RegExp function from the context in which the
1413 // function was created. We do not use the RegExp function from the
1414 // current global context because this might be the RegExp function
1415 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001416 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001417 Handle<JSFunction>(
1418 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001419 // Compute the regular expression literal.
1420 bool has_pending_exception;
1421 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001422 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1423 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001424 if (has_pending_exception) {
1425 ASSERT(Top::has_pending_exception());
1426 return Failure::Exception();
1427 }
1428 literals->set(index, *regexp);
1429 return *regexp;
1430}
1431
1432
1433static Object* Runtime_FunctionGetName(Arguments args) {
1434 NoHandleAllocation ha;
1435 ASSERT(args.length() == 1);
1436
1437 CONVERT_CHECKED(JSFunction, f, args[0]);
1438 return f->shared()->name();
1439}
1440
1441
ager@chromium.org236ad962008-09-25 09:45:57 +00001442static Object* Runtime_FunctionSetName(Arguments args) {
1443 NoHandleAllocation ha;
1444 ASSERT(args.length() == 2);
1445
1446 CONVERT_CHECKED(JSFunction, f, args[0]);
1447 CONVERT_CHECKED(String, name, args[1]);
1448 f->shared()->set_name(name);
1449 return Heap::undefined_value();
1450}
1451
1452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001453static Object* Runtime_FunctionGetScript(Arguments args) {
1454 HandleScope scope;
1455 ASSERT(args.length() == 1);
1456
1457 CONVERT_CHECKED(JSFunction, fun, args[0]);
1458 Handle<Object> script = Handle<Object>(fun->shared()->script());
1459 if (!script->IsScript()) return Heap::undefined_value();
1460
1461 return *GetScriptWrapper(Handle<Script>::cast(script));
1462}
1463
1464
1465static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1466 NoHandleAllocation ha;
1467 ASSERT(args.length() == 1);
1468
1469 CONVERT_CHECKED(JSFunction, f, args[0]);
1470 return f->shared()->GetSourceCode();
1471}
1472
1473
1474static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1475 NoHandleAllocation ha;
1476 ASSERT(args.length() == 1);
1477
1478 CONVERT_CHECKED(JSFunction, fun, args[0]);
1479 int pos = fun->shared()->start_position();
1480 return Smi::FromInt(pos);
1481}
1482
1483
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001484static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1485 ASSERT(args.length() == 2);
1486
1487 CONVERT_CHECKED(JSFunction, fun, args[0]);
1488 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1489
1490 Code* code = fun->code();
1491 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1492
1493 Address pc = code->address() + offset;
1494 return Smi::FromInt(fun->code()->SourcePosition(pc));
1495}
1496
1497
1498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001499static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1500 NoHandleAllocation ha;
1501 ASSERT(args.length() == 2);
1502
1503 CONVERT_CHECKED(JSFunction, fun, args[0]);
1504 CONVERT_CHECKED(String, name, args[1]);
1505 fun->SetInstanceClassName(name);
1506 return Heap::undefined_value();
1507}
1508
1509
1510static Object* Runtime_FunctionSetLength(Arguments args) {
1511 NoHandleAllocation ha;
1512 ASSERT(args.length() == 2);
1513
1514 CONVERT_CHECKED(JSFunction, fun, args[0]);
1515 CONVERT_CHECKED(Smi, length, args[1]);
1516 fun->shared()->set_length(length->value());
1517 return length;
1518}
1519
1520
1521static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001522 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001523 ASSERT(args.length() == 2);
1524
1525 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001526 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1527 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001528 return args[0]; // return TOS
1529}
1530
1531
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001532static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1533 NoHandleAllocation ha;
1534 ASSERT(args.length() == 1);
1535
1536 CONVERT_CHECKED(JSFunction, f, args[0]);
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001537 return f->shared()->IsApiFunction() ? Heap::true_value()
1538 : Heap::false_value();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001539}
1540
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001541static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1542 NoHandleAllocation ha;
1543 ASSERT(args.length() == 1);
1544
1545 CONVERT_CHECKED(JSFunction, f, args[0]);
1546 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1547}
1548
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001549
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001550static Object* Runtime_SetCode(Arguments args) {
1551 HandleScope scope;
1552 ASSERT(args.length() == 2);
1553
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001554 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001555 Handle<Object> code = args.at<Object>(1);
1556
1557 Handle<Context> context(target->context());
1558
1559 if (!code->IsNull()) {
1560 RUNTIME_ASSERT(code->IsJSFunction());
1561 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001562 Handle<SharedFunctionInfo> shared(fun->shared());
1563 SetExpectedNofProperties(target, shared->expected_nof_properties());
1564
1565 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001566 return Failure::Exception();
1567 }
1568 // Set the code, formal parameter count, and the length of the target
1569 // function.
1570 target->set_code(fun->code());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001571 target->shared()->set_length(shared->length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001572 target->shared()->set_formal_parameter_count(
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001573 shared->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001574 // Set the source code of the target function to undefined.
1575 // SetCode is only used for built-in constructors like String,
1576 // Array, and Object, and some web code
1577 // doesn't like seeing source code for constructors.
1578 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001579 // Clear the optimization hints related to the compiled code as these are no
1580 // longer valid when the code is overwritten.
1581 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001582 context = Handle<Context>(fun->context());
1583
1584 // Make sure we get a fresh copy of the literal vector to avoid
1585 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001586 int number_of_literals = fun->NumberOfLiterals();
1587 Handle<FixedArray> literals =
1588 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001589 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001590 // Insert the object, regexp and array functions in the literals
1591 // array prefix. These are the functions that will be used when
1592 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001593 literals->set(JSFunction::kLiteralGlobalContextIndex,
1594 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001595 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001596 // It's okay to skip the write barrier here because the literals
1597 // are guaranteed to be in old space.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001598 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001599 }
1600
1601 target->set_context(*context);
1602 return *target;
1603}
1604
1605
1606static Object* CharCodeAt(String* subject, Object* index) {
1607 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001608 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001609 // Flatten the string. If someone wants to get a char at an index
1610 // in a cons string, it is likely that more indices will be
1611 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001612 Object* flat = subject->TryFlatten();
1613 if (flat->IsFailure()) return flat;
1614 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001615 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001616 return Heap::nan_value();
1617 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001618 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001619}
1620
1621
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001622static Object* CharFromCode(Object* char_code) {
1623 uint32_t code;
1624 if (Array::IndexFromObject(char_code, &code)) {
1625 if (code <= 0xffff) {
1626 return Heap::LookupSingleCharacterStringFromCode(code);
1627 }
1628 }
1629 return Heap::empty_string();
1630}
1631
1632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001633static Object* Runtime_StringCharCodeAt(Arguments args) {
1634 NoHandleAllocation ha;
1635 ASSERT(args.length() == 2);
1636
1637 CONVERT_CHECKED(String, subject, args[0]);
1638 Object* index = args[1];
1639 return CharCodeAt(subject, index);
1640}
1641
1642
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001643static Object* Runtime_StringCharAt(Arguments args) {
1644 NoHandleAllocation ha;
1645 ASSERT(args.length() == 2);
1646
1647 CONVERT_CHECKED(String, subject, args[0]);
1648 Object* index = args[1];
kasperl@chromium.org74e4e5e2010-01-25 10:15:52 +00001649 Object* code = CharCodeAt(subject, index);
1650 if (code == Heap::nan_value()) {
1651 return Heap::undefined_value();
1652 }
1653 return CharFromCode(code);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001654}
1655
1656
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001657static Object* Runtime_CharFromCode(Arguments args) {
1658 NoHandleAllocation ha;
1659 ASSERT(args.length() == 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001660 return CharFromCode(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001661}
1662
lrn@chromium.org25156de2010-04-06 13:10:27 +00001663
1664class FixedArrayBuilder {
1665 public:
1666 explicit FixedArrayBuilder(int initial_capacity)
1667 : array_(Factory::NewFixedArrayWithHoles(initial_capacity)),
1668 length_(0) {
1669 // Require a non-zero initial size. Ensures that doubling the size to
1670 // extend the array will work.
1671 ASSERT(initial_capacity > 0);
1672 }
1673
1674 explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
1675 : array_(backing_store),
1676 length_(0) {
1677 // Require a non-zero initial size. Ensures that doubling the size to
1678 // extend the array will work.
1679 ASSERT(backing_store->length() > 0);
1680 }
1681
1682 bool HasCapacity(int elements) {
1683 int length = array_->length();
1684 int required_length = length_ + elements;
1685 return (length >= required_length);
1686 }
1687
1688 void EnsureCapacity(int elements) {
1689 int length = array_->length();
1690 int required_length = length_ + elements;
1691 if (length < required_length) {
1692 int new_length = length;
1693 do {
1694 new_length *= 2;
1695 } while (new_length < required_length);
1696 Handle<FixedArray> extended_array =
1697 Factory::NewFixedArrayWithHoles(new_length);
1698 array_->CopyTo(0, *extended_array, 0, length_);
1699 array_ = extended_array;
1700 }
1701 }
1702
1703 void Add(Object* value) {
1704 ASSERT(length_ < capacity());
1705 array_->set(length_, value);
1706 length_++;
1707 }
1708
1709 void Add(Smi* value) {
1710 ASSERT(length_ < capacity());
1711 array_->set(length_, value);
1712 length_++;
1713 }
1714
1715 Handle<FixedArray> array() {
1716 return array_;
1717 }
1718
1719 int length() {
1720 return length_;
1721 }
1722
1723 int capacity() {
1724 return array_->length();
1725 }
1726
1727 Handle<JSArray> ToJSArray() {
1728 Handle<JSArray> result_array = Factory::NewJSArrayWithElements(array_);
1729 result_array->set_length(Smi::FromInt(length_));
1730 return result_array;
1731 }
1732
1733 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
1734 target_array->set_elements(*array_);
1735 target_array->set_length(Smi::FromInt(length_));
1736 return target_array;
1737 }
1738
1739 private:
1740 Handle<FixedArray> array_;
1741 int length_;
1742};
1743
1744
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001745// Forward declarations.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001746const int kStringBuilderConcatHelperLengthBits = 11;
1747const int kStringBuilderConcatHelperPositionBits = 19;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001748
1749template <typename schar>
1750static inline void StringBuilderConcatHelper(String*,
1751 schar*,
1752 FixedArray*,
1753 int);
1754
lrn@chromium.org25156de2010-04-06 13:10:27 +00001755typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
1756 StringBuilderSubstringLength;
1757typedef BitField<int,
1758 kStringBuilderConcatHelperLengthBits,
1759 kStringBuilderConcatHelperPositionBits>
1760 StringBuilderSubstringPosition;
1761
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001762
1763class ReplacementStringBuilder {
1764 public:
1765 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
lrn@chromium.org25156de2010-04-06 13:10:27 +00001766 : array_builder_(estimated_part_count),
1767 subject_(subject),
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001768 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001769 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001770 // Require a non-zero initial size. Ensures that doubling the size to
1771 // extend the array will work.
1772 ASSERT(estimated_part_count > 0);
1773 }
1774
lrn@chromium.org25156de2010-04-06 13:10:27 +00001775 static inline void AddSubjectSlice(FixedArrayBuilder* builder,
1776 int from,
1777 int to) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001778 ASSERT(from >= 0);
1779 int length = to - from;
1780 ASSERT(length > 0);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001781 if (StringBuilderSubstringLength::is_valid(length) &&
1782 StringBuilderSubstringPosition::is_valid(from)) {
1783 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1784 StringBuilderSubstringPosition::encode(from);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001785 builder->Add(Smi::FromInt(encoded_slice));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001786 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001787 // Otherwise encode as two smis.
lrn@chromium.org25156de2010-04-06 13:10:27 +00001788 builder->Add(Smi::FromInt(-length));
1789 builder->Add(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001790 }
lrn@chromium.org25156de2010-04-06 13:10:27 +00001791 }
1792
1793
1794 void EnsureCapacity(int elements) {
1795 array_builder_.EnsureCapacity(elements);
1796 }
1797
1798
1799 void AddSubjectSlice(int from, int to) {
1800 AddSubjectSlice(&array_builder_, from, to);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001801 IncrementCharacterCount(to - from);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001802 }
1803
1804
1805 void AddString(Handle<String> string) {
1806 int length = string->length();
1807 ASSERT(length > 0);
1808 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001809 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001810 is_ascii_ = false;
1811 }
1812 IncrementCharacterCount(length);
1813 }
1814
1815
1816 Handle<String> ToString() {
lrn@chromium.org25156de2010-04-06 13:10:27 +00001817 if (array_builder_.length() == 0) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001818 return Factory::empty_string();
1819 }
1820
1821 Handle<String> joined_string;
1822 if (is_ascii_) {
1823 joined_string = NewRawAsciiString(character_count_);
1824 AssertNoAllocation no_alloc;
1825 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1826 char* char_buffer = seq->GetChars();
1827 StringBuilderConcatHelper(*subject_,
1828 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001829 *array_builder_.array(),
1830 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001831 } else {
1832 // Non-ASCII.
1833 joined_string = NewRawTwoByteString(character_count_);
1834 AssertNoAllocation no_alloc;
1835 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1836 uc16* char_buffer = seq->GetChars();
1837 StringBuilderConcatHelper(*subject_,
1838 char_buffer,
lrn@chromium.org25156de2010-04-06 13:10:27 +00001839 *array_builder_.array(),
1840 array_builder_.length());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001841 }
1842 return joined_string;
1843 }
1844
1845
1846 void IncrementCharacterCount(int by) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001847 if (character_count_ > String::kMaxLength - by) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001848 V8::FatalProcessOutOfMemory("String.replace result too large.");
1849 }
1850 character_count_ += by;
1851 }
1852
lrn@chromium.org25156de2010-04-06 13:10:27 +00001853 Handle<JSArray> GetParts() {
1854 Handle<JSArray> result =
1855 Factory::NewJSArrayWithElements(array_builder_.array());
1856 result->set_length(Smi::FromInt(array_builder_.length()));
1857 return result;
1858 }
kmillikin@chromium.orgd9825192010-03-30 08:36:16 +00001859
lrn@chromium.org25156de2010-04-06 13:10:27 +00001860 private:
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001861 Handle<String> NewRawAsciiString(int size) {
1862 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1863 }
1864
1865
1866 Handle<String> NewRawTwoByteString(int size) {
1867 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1868 }
1869
1870
1871 void AddElement(Object* element) {
1872 ASSERT(element->IsSmi() || element->IsString());
lrn@chromium.org25156de2010-04-06 13:10:27 +00001873 ASSERT(array_builder_.capacity() > array_builder_.length());
1874 array_builder_.Add(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001875 }
1876
lrn@chromium.org25156de2010-04-06 13:10:27 +00001877 FixedArrayBuilder array_builder_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001878 Handle<String> subject_;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001879 int character_count_;
1880 bool is_ascii_;
1881};
1882
1883
1884class CompiledReplacement {
1885 public:
1886 CompiledReplacement()
1887 : parts_(1), replacement_substrings_(0) {}
1888
1889 void Compile(Handle<String> replacement,
1890 int capture_count,
1891 int subject_length);
1892
1893 void Apply(ReplacementStringBuilder* builder,
1894 int match_from,
1895 int match_to,
1896 Handle<JSArray> last_match_info);
1897
1898 // Number of distinct parts of the replacement pattern.
1899 int parts() {
1900 return parts_.length();
1901 }
1902 private:
1903 enum PartType {
1904 SUBJECT_PREFIX = 1,
1905 SUBJECT_SUFFIX,
1906 SUBJECT_CAPTURE,
1907 REPLACEMENT_SUBSTRING,
1908 REPLACEMENT_STRING,
1909
1910 NUMBER_OF_PART_TYPES
1911 };
1912
1913 struct ReplacementPart {
1914 static inline ReplacementPart SubjectMatch() {
1915 return ReplacementPart(SUBJECT_CAPTURE, 0);
1916 }
1917 static inline ReplacementPart SubjectCapture(int capture_index) {
1918 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1919 }
1920 static inline ReplacementPart SubjectPrefix() {
1921 return ReplacementPart(SUBJECT_PREFIX, 0);
1922 }
1923 static inline ReplacementPart SubjectSuffix(int subject_length) {
1924 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1925 }
1926 static inline ReplacementPart ReplacementString() {
1927 return ReplacementPart(REPLACEMENT_STRING, 0);
1928 }
1929 static inline ReplacementPart ReplacementSubString(int from, int to) {
1930 ASSERT(from >= 0);
1931 ASSERT(to > from);
1932 return ReplacementPart(-from, to);
1933 }
1934
1935 // If tag <= 0 then it is the negation of a start index of a substring of
1936 // the replacement pattern, otherwise it's a value from PartType.
1937 ReplacementPart(int tag, int data)
1938 : tag(tag), data(data) {
1939 // Must be non-positive or a PartType value.
1940 ASSERT(tag < NUMBER_OF_PART_TYPES);
1941 }
1942 // Either a value of PartType or a non-positive number that is
1943 // the negation of an index into the replacement string.
1944 int tag;
1945 // The data value's interpretation depends on the value of tag:
1946 // tag == SUBJECT_PREFIX ||
1947 // tag == SUBJECT_SUFFIX: data is unused.
1948 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1949 // tag == REPLACEMENT_SUBSTRING ||
1950 // tag == REPLACEMENT_STRING: data is index into array of substrings
1951 // of the replacement string.
1952 // tag <= 0: Temporary representation of the substring of the replacement
1953 // string ranging over -tag .. data.
1954 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1955 // substring objects.
1956 int data;
1957 };
1958
1959 template<typename Char>
1960 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1961 Vector<Char> characters,
1962 int capture_count,
1963 int subject_length) {
1964 int length = characters.length();
1965 int last = 0;
1966 for (int i = 0; i < length; i++) {
1967 Char c = characters[i];
1968 if (c == '$') {
1969 int next_index = i + 1;
1970 if (next_index == length) { // No next character!
1971 break;
1972 }
1973 Char c2 = characters[next_index];
1974 switch (c2) {
1975 case '$':
1976 if (i > last) {
1977 // There is a substring before. Include the first "$".
1978 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1979 last = next_index + 1; // Continue after the second "$".
1980 } else {
1981 // Let the next substring start with the second "$".
1982 last = next_index;
1983 }
1984 i = next_index;
1985 break;
1986 case '`':
1987 if (i > last) {
1988 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1989 }
1990 parts->Add(ReplacementPart::SubjectPrefix());
1991 i = next_index;
1992 last = i + 1;
1993 break;
1994 case '\'':
1995 if (i > last) {
1996 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1997 }
1998 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1999 i = next_index;
2000 last = i + 1;
2001 break;
2002 case '&':
2003 if (i > last) {
2004 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2005 }
2006 parts->Add(ReplacementPart::SubjectMatch());
2007 i = next_index;
2008 last = i + 1;
2009 break;
2010 case '0':
2011 case '1':
2012 case '2':
2013 case '3':
2014 case '4':
2015 case '5':
2016 case '6':
2017 case '7':
2018 case '8':
2019 case '9': {
2020 int capture_ref = c2 - '0';
2021 if (capture_ref > capture_count) {
2022 i = next_index;
2023 continue;
2024 }
2025 int second_digit_index = next_index + 1;
2026 if (second_digit_index < length) {
2027 // Peek ahead to see if we have two digits.
2028 Char c3 = characters[second_digit_index];
2029 if ('0' <= c3 && c3 <= '9') { // Double digits.
2030 int double_digit_ref = capture_ref * 10 + c3 - '0';
2031 if (double_digit_ref <= capture_count) {
2032 next_index = second_digit_index;
2033 capture_ref = double_digit_ref;
2034 }
2035 }
2036 }
2037 if (capture_ref > 0) {
2038 if (i > last) {
2039 parts->Add(ReplacementPart::ReplacementSubString(last, i));
2040 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002041 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002042 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
2043 last = next_index + 1;
2044 }
2045 i = next_index;
2046 break;
2047 }
2048 default:
2049 i = next_index;
2050 break;
2051 }
2052 }
2053 }
2054 if (length > last) {
2055 if (last == 0) {
2056 parts->Add(ReplacementPart::ReplacementString());
2057 } else {
2058 parts->Add(ReplacementPart::ReplacementSubString(last, length));
2059 }
2060 }
2061 }
2062
2063 ZoneList<ReplacementPart> parts_;
2064 ZoneList<Handle<String> > replacement_substrings_;
2065};
2066
2067
2068void CompiledReplacement::Compile(Handle<String> replacement,
2069 int capture_count,
2070 int subject_length) {
2071 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00002072 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002073 AssertNoAllocation no_alloc;
2074 ParseReplacementPattern(&parts_,
2075 replacement->ToAsciiVector(),
2076 capture_count,
2077 subject_length);
2078 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00002079 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002080 AssertNoAllocation no_alloc;
2081
2082 ParseReplacementPattern(&parts_,
2083 replacement->ToUC16Vector(),
2084 capture_count,
2085 subject_length);
2086 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002087 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002088 int substring_index = 0;
2089 for (int i = 0, n = parts_.length(); i < n; i++) {
2090 int tag = parts_[i].tag;
2091 if (tag <= 0) { // A replacement string slice.
2092 int from = -tag;
2093 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002094 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002095 parts_[i].tag = REPLACEMENT_SUBSTRING;
2096 parts_[i].data = substring_index;
2097 substring_index++;
2098 } else if (tag == REPLACEMENT_STRING) {
2099 replacement_substrings_.Add(replacement);
2100 parts_[i].data = substring_index;
2101 substring_index++;
2102 }
2103 }
2104}
2105
2106
2107void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
2108 int match_from,
2109 int match_to,
2110 Handle<JSArray> last_match_info) {
2111 for (int i = 0, n = parts_.length(); i < n; i++) {
2112 ReplacementPart part = parts_[i];
2113 switch (part.tag) {
2114 case SUBJECT_PREFIX:
2115 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
2116 break;
2117 case SUBJECT_SUFFIX: {
2118 int subject_length = part.data;
2119 if (match_to < subject_length) {
2120 builder->AddSubjectSlice(match_to, subject_length);
2121 }
2122 break;
2123 }
2124 case SUBJECT_CAPTURE: {
2125 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002126 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002127 int from = RegExpImpl::GetCapture(match_info, capture * 2);
2128 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
2129 if (from >= 0 && to > from) {
2130 builder->AddSubjectSlice(from, to);
2131 }
2132 break;
2133 }
2134 case REPLACEMENT_SUBSTRING:
2135 case REPLACEMENT_STRING:
2136 builder->AddString(replacement_substrings_[part.data]);
2137 break;
2138 default:
2139 UNREACHABLE();
2140 }
2141 }
2142}
2143
2144
2145
2146static Object* StringReplaceRegExpWithString(String* subject,
2147 JSRegExp* regexp,
2148 String* replacement,
2149 JSArray* last_match_info) {
2150 ASSERT(subject->IsFlat());
2151 ASSERT(replacement->IsFlat());
2152
2153 HandleScope handles;
2154
2155 int length = subject->length();
2156 Handle<String> subject_handle(subject);
2157 Handle<JSRegExp> regexp_handle(regexp);
2158 Handle<String> replacement_handle(replacement);
2159 Handle<JSArray> last_match_info_handle(last_match_info);
2160 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
2161 subject_handle,
2162 0,
2163 last_match_info_handle);
2164 if (match.is_null()) {
2165 return Failure::Exception();
2166 }
2167 if (match->IsNull()) {
2168 return *subject_handle;
2169 }
2170
2171 int capture_count = regexp_handle->CaptureCount();
2172
2173 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002174 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002175 CompiledReplacement compiled_replacement;
2176 compiled_replacement.Compile(replacement_handle,
2177 capture_count,
2178 length);
2179
2180 bool is_global = regexp_handle->GetFlags().is_global();
2181
2182 // Guessing the number of parts that the final result string is built
2183 // from. Global regexps can match any number of times, so we guess
2184 // conservatively.
2185 int expected_parts =
2186 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
2187 ReplacementStringBuilder builder(subject_handle, expected_parts);
2188
2189 // Index of end of last match.
2190 int prev = 0;
2191
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002192 // Number of parts added by compiled replacement plus preceeding
2193 // string and possibly suffix after last match. It is possible for
2194 // all components to use two elements when encoded as two smis.
2195 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002196 bool matched = true;
2197 do {
2198 ASSERT(last_match_info_handle->HasFastElements());
2199 // Increase the capacity of the builder before entering local handle-scope,
2200 // so its internal buffer can safely allocate a new handle if it grows.
2201 builder.EnsureCapacity(parts_added_per_loop);
2202
2203 HandleScope loop_scope;
2204 int start, end;
2205 {
2206 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002207 FixedArray* match_info_array =
2208 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002209
2210 ASSERT_EQ(capture_count * 2 + 2,
2211 RegExpImpl::GetLastCaptureCount(match_info_array));
2212 start = RegExpImpl::GetCapture(match_info_array, 0);
2213 end = RegExpImpl::GetCapture(match_info_array, 1);
2214 }
2215
2216 if (prev < start) {
2217 builder.AddSubjectSlice(prev, start);
2218 }
2219 compiled_replacement.Apply(&builder,
2220 start,
2221 end,
2222 last_match_info_handle);
2223 prev = end;
2224
2225 // Only continue checking for global regexps.
2226 if (!is_global) break;
2227
2228 // Continue from where the match ended, unless it was an empty match.
2229 int next = end;
2230 if (start == end) {
2231 next = end + 1;
2232 if (next > length) break;
2233 }
2234
2235 match = RegExpImpl::Exec(regexp_handle,
2236 subject_handle,
2237 next,
2238 last_match_info_handle);
2239 if (match.is_null()) {
2240 return Failure::Exception();
2241 }
2242 matched = !match->IsNull();
2243 } while (matched);
2244
2245 if (prev < length) {
2246 builder.AddSubjectSlice(prev, length);
2247 }
2248
2249 return *(builder.ToString());
2250}
2251
2252
2253static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
2254 ASSERT(args.length() == 4);
2255
2256 CONVERT_CHECKED(String, subject, args[0]);
2257 if (!subject->IsFlat()) {
2258 Object* flat_subject = subject->TryFlatten();
2259 if (flat_subject->IsFailure()) {
2260 return flat_subject;
2261 }
2262 subject = String::cast(flat_subject);
2263 }
2264
2265 CONVERT_CHECKED(String, replacement, args[2]);
2266 if (!replacement->IsFlat()) {
2267 Object* flat_replacement = replacement->TryFlatten();
2268 if (flat_replacement->IsFailure()) {
2269 return flat_replacement;
2270 }
2271 replacement = String::cast(flat_replacement);
2272 }
2273
2274 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
2275 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
2276
2277 ASSERT(last_match_info->HasFastElements());
2278
2279 return StringReplaceRegExpWithString(subject,
2280 regexp,
2281 replacement,
2282 last_match_info);
2283}
2284
2285
ager@chromium.org7c537e22008-10-16 08:43:32 +00002286// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
2287// limit, we can fix the size of tables.
2288static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002289// Reduce alphabet to this size.
2290static const int kBMAlphabetSize = 0x100;
2291// For patterns below this length, the skip length of Boyer-Moore is too short
2292// to compensate for the algorithmic overhead compared to simple brute force.
2293static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002294
ager@chromium.org7c537e22008-10-16 08:43:32 +00002295// Holds the two buffers used by Boyer-Moore string search's Good Suffix
2296// shift. Only allows the last kBMMaxShift characters of the needle
2297// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002298class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002299 public:
2300 BMGoodSuffixBuffers() {}
2301 inline void init(int needle_length) {
2302 ASSERT(needle_length > 1);
2303 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
2304 int len = needle_length - start;
2305 biased_suffixes_ = suffixes_ - start;
2306 biased_good_suffix_shift_ = good_suffix_shift_ - start;
2307 for (int i = 0; i <= len; i++) {
2308 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002309 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002310 }
2311 inline int& suffix(int index) {
2312 ASSERT(biased_suffixes_ + index >= suffixes_);
2313 return biased_suffixes_[index];
2314 }
2315 inline int& shift(int index) {
2316 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
2317 return biased_good_suffix_shift_[index];
2318 }
2319 private:
2320 int suffixes_[kBMMaxShift + 1];
2321 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002322 int* biased_suffixes_;
2323 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002324 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
2325};
2326
2327// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002328static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00002329static BMGoodSuffixBuffers bmgs_buffers;
2330
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002331// State of the string match tables.
2332// SIMPLE: No usable content in the buffers.
2333// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
2334// BOYER_MOORE: The bmgs_buffers tables have also been populated.
2335// Whenever starting with a new needle, one should call InitializeStringSearch
2336// to determine which search strategy to use, and in the case of a long-needle
2337// strategy, the call also initializes the algorithm to SIMPLE.
2338enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
2339static StringSearchAlgorithm algorithm;
2340
2341
ager@chromium.org7c537e22008-10-16 08:43:32 +00002342// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002343template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002344static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
2345 // Only preprocess at most kBMMaxShift last characters of pattern.
2346 int start = pattern.length() < kBMMaxShift ? 0
2347 : pattern.length() - kBMMaxShift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002348 // Run forwards to populate bad_char_table, so that *last* instance
2349 // of character equivalence class is the one registered.
2350 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002351 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
2352 : kBMAlphabetSize;
2353 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002354 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002355 } else {
2356 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002357 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002358 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002359 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002360 for (int i = start; i < pattern.length() - 1; i++) {
2361 pchar c = pattern[i];
2362 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002363 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002364 }
2365}
2366
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002367
ager@chromium.org7c537e22008-10-16 08:43:32 +00002368template <typename pchar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002369static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002370 int m = pattern.length();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002371 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002372 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002373 // Compute Good Suffix tables.
2374 bmgs_buffers.init(m);
2375
2376 bmgs_buffers.shift(m-1) = 1;
2377 bmgs_buffers.suffix(m) = m + 1;
2378 pchar last_char = pattern[m - 1];
2379 int suffix = m + 1;
2380 for (int i = m; i > start;) {
2381 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
2382 if (bmgs_buffers.shift(suffix) == len) {
2383 bmgs_buffers.shift(suffix) = suffix - i;
2384 }
2385 suffix = bmgs_buffers.suffix(suffix);
2386 }
2387 i--;
2388 suffix--;
2389 bmgs_buffers.suffix(i) = suffix;
2390 if (suffix == m) {
2391 // No suffix to extend, so we check against last_char only.
2392 while (i > start && pattern[i - 1] != last_char) {
2393 if (bmgs_buffers.shift(m) == len) {
2394 bmgs_buffers.shift(m) = m - i;
2395 }
2396 i--;
2397 bmgs_buffers.suffix(i) = m;
2398 }
2399 if (i > start) {
2400 i--;
2401 suffix--;
2402 bmgs_buffers.suffix(i) = suffix;
2403 }
2404 }
2405 }
2406 if (suffix < m) {
2407 for (int i = start; i <= m; i++) {
2408 if (bmgs_buffers.shift(i) == len) {
2409 bmgs_buffers.shift(i) = suffix - start;
2410 }
2411 if (i == suffix) {
2412 suffix = bmgs_buffers.suffix(suffix);
2413 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414 }
2415 }
2416}
2417
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002418
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002419template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002420static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002421 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002422 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002423 }
2424 if (sizeof(pchar) == 1) {
2425 if (char_code > String::kMaxAsciiCharCode) {
2426 return -1;
2427 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002428 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002429 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002430 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002431}
2432
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002433
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002434// Restricted simplified Boyer-Moore string matching.
2435// Uses only the bad-shift table of Boyer-Moore and only uses it
2436// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002437template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002438static int BoyerMooreHorspool(Vector<const schar> subject,
2439 Vector<const pchar> pattern,
2440 int start_index,
2441 bool* complete) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002442 ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002443 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002444 int m = pattern.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002445
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002446 int badness = -m;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002447
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002448 // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002449 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002450 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002451 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002452 // Perform search
2453 for (idx = start_index; idx <= n - m;) {
2454 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002455 int c;
2456 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002457 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002458 int shift = j - bc_occ;
2459 idx += shift;
2460 badness += 1 - shift; // at most zero, so badness cannot increase.
2461 if (idx > n - m) {
2462 *complete = true;
2463 return -1;
2464 }
2465 }
2466 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002467 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002468 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002469 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002470 return idx;
2471 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002472 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002473 // Badness increases by the number of characters we have
2474 // checked, and decreases by the number of characters we
2475 // can skip by shifting. It's a measure of how we are doing
2476 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002477 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002478 if (badness > 0) {
2479 *complete = false;
2480 return idx;
2481 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002482 }
2483 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002484 *complete = true;
2485 return -1;
2486}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002487
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002488
2489template <typename schar, typename pchar>
2490static int BoyerMooreIndexOf(Vector<const schar> subject,
2491 Vector<const pchar> pattern,
2492 int idx) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002493 ASSERT(algorithm <= BOYER_MOORE);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002494 int n = subject.length();
2495 int m = pattern.length();
2496 // Only preprocess at most kBMMaxShift last characters of pattern.
2497 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2498
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002499 pchar last_char = pattern[m - 1];
2500 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002501 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002502 int j = m - 1;
2503 schar c;
2504 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002505 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002506 idx += shift;
2507 if (idx > n - m) {
2508 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002509 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002510 }
2511 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2512 if (j < 0) {
2513 return idx;
2514 } else if (j < start) {
2515 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002516 // Fall back on BMH shift.
2517 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002518 } else {
2519 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002520 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002521 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002522 if (gs_shift > shift) {
2523 shift = gs_shift;
2524 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002525 idx += shift;
2526 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002527 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002528
2529 return -1;
2530}
2531
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002532
2533template <typename schar>
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002534static inline int SingleCharIndexOf(Vector<const schar> string,
2535 schar pattern_char,
2536 int start_index) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002537 if (sizeof(schar) == 1) {
2538 const schar* pos = reinterpret_cast<const schar*>(
2539 memchr(string.start() + start_index,
2540 pattern_char,
2541 string.length() - start_index));
2542 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002543 return static_cast<int>(pos - string.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002544 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002545 for (int i = start_index, n = string.length(); i < n; i++) {
2546 if (pattern_char == string[i]) {
2547 return i;
2548 }
2549 }
2550 return -1;
2551}
2552
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002553
2554template <typename schar>
2555static int SingleCharLastIndexOf(Vector<const schar> string,
2556 schar pattern_char,
2557 int start_index) {
2558 for (int i = start_index; i >= 0; i--) {
2559 if (pattern_char == string[i]) {
2560 return i;
2561 }
2562 }
2563 return -1;
2564}
2565
2566
ager@chromium.org7c537e22008-10-16 08:43:32 +00002567// Trivial string search for shorter strings.
2568// On return, if "complete" is set to true, the return value is the
2569// final result of searching for the patter in the subject.
2570// If "complete" is set to false, the return value is the index where
2571// further checking should start, i.e., it's guaranteed that the pattern
2572// does not occur at a position prior to the returned index.
2573template <typename pchar, typename schar>
2574static int SimpleIndexOf(Vector<const schar> subject,
2575 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002576 int idx,
2577 bool* complete) {
2578 // Badness is a count of how much work we have done. When we have
2579 // done enough work we decide it's probably worth switching to a better
2580 // algorithm.
2581 int badness = -10 - (pattern.length() << 2);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002582
ager@chromium.org7c537e22008-10-16 08:43:32 +00002583 // We know our pattern is at least 2 characters, we cache the first so
2584 // the common case of the first character not matching is faster.
2585 pchar pattern_first_char = pattern[0];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002586 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2587 badness++;
2588 if (badness > 0) {
2589 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002590 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002591 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002592 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2593 const schar* pos = reinterpret_cast<const schar*>(
2594 memchr(subject.start() + i,
2595 pattern_first_char,
2596 n - i + 1));
2597 if (pos == NULL) {
2598 *complete = true;
2599 return -1;
2600 }
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002601 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002602 } else {
2603 if (subject[i] != pattern_first_char) continue;
2604 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002605 int j = 1;
2606 do {
2607 if (pattern[j] != subject[i+j]) {
2608 break;
2609 }
2610 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002611 } while (j < pattern.length());
2612 if (j == pattern.length()) {
2613 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002614 return i;
2615 }
2616 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002617 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002618 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002619 return -1;
2620}
2621
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002622// Simple indexOf that never bails out. For short patterns only.
2623template <typename pchar, typename schar>
2624static int SimpleIndexOf(Vector<const schar> subject,
2625 Vector<const pchar> pattern,
2626 int idx) {
2627 pchar pattern_first_char = pattern[0];
2628 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002629 if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
2630 const schar* pos = reinterpret_cast<const schar*>(
2631 memchr(subject.start() + i,
2632 pattern_first_char,
2633 n - i + 1));
2634 if (pos == NULL) return -1;
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002635 i = static_cast<int>(pos - subject.start());
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00002636 } else {
2637 if (subject[i] != pattern_first_char) continue;
2638 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002639 int j = 1;
2640 do {
2641 if (pattern[j] != subject[i+j]) {
2642 break;
2643 }
2644 j++;
2645 } while (j < pattern.length());
2646 if (j == pattern.length()) {
2647 return i;
2648 }
2649 }
2650 return -1;
2651}
2652
2653
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002654// Strategy for searching for a string in another string.
2655enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
ager@chromium.org7c537e22008-10-16 08:43:32 +00002656
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002657
2658template <typename pchar>
2659static inline StringSearchStrategy InitializeStringSearch(
2660 Vector<const pchar> pat, bool ascii_subject) {
2661 ASSERT(pat.length() > 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002662 // We have an ASCII haystack and a non-ASCII needle. Check if there
2663 // really is a non-ASCII character in the needle and bail out if there
2664 // is.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002665 if (ascii_subject && sizeof(pchar) > 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002666 for (int i = 0; i < pat.length(); i++) {
2667 uc16 c = pat[i];
2668 if (c > String::kMaxAsciiCharCode) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002669 return SEARCH_FAIL;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002670 }
2671 }
2672 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002673 if (pat.length() < kBMMinPatternLength) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002674 return SEARCH_SHORT;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002675 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002676 algorithm = SIMPLE_SEARCH;
2677 return SEARCH_LONG;
2678}
2679
2680
2681// Dispatch long needle searches to different algorithms.
2682template <typename schar, typename pchar>
2683static int ComplexIndexOf(Vector<const schar> sub,
2684 Vector<const pchar> pat,
2685 int start_index) {
2686 ASSERT(pat.length() >= kBMMinPatternLength);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002687 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002688 bool complete;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002689 int idx = start_index;
2690 switch (algorithm) {
2691 case SIMPLE_SEARCH:
2692 idx = SimpleIndexOf(sub, pat, idx, &complete);
2693 if (complete) return idx;
2694 BoyerMoorePopulateBadCharTable(pat);
2695 algorithm = BOYER_MOORE_HORSPOOL;
2696 // FALLTHROUGH.
2697 case BOYER_MOORE_HORSPOOL:
2698 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
2699 if (complete) return idx;
2700 // Build the Good Suffix table and continue searching.
2701 BoyerMoorePopulateGoodSuffixTable(pat);
2702 algorithm = BOYER_MOORE;
2703 // FALLTHROUGH.
2704 case BOYER_MOORE:
2705 return BoyerMooreIndexOf(sub, pat, idx);
2706 }
2707 UNREACHABLE();
2708 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002709}
2710
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002711
2712// Dispatch to different search strategies for a single search.
2713// If searching multiple times on the same needle, the search
2714// strategy should only be computed once and then dispatch to different
2715// loops.
2716template <typename schar, typename pchar>
2717static int StringSearch(Vector<const schar> sub,
2718 Vector<const pchar> pat,
2719 int start_index) {
2720 bool ascii_subject = (sizeof(schar) == 1);
2721 StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
2722 switch (strategy) {
2723 case SEARCH_FAIL: return -1;
2724 case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
2725 case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
2726 }
2727 UNREACHABLE();
2728 return -1;
2729}
2730
2731
ager@chromium.org7c537e22008-10-16 08:43:32 +00002732// Perform string match of pattern on subject, starting at start index.
2733// Caller must ensure that 0 <= start_index <= sub->length(),
2734// and should check that pat->length() + start_index <= sub->length()
2735int Runtime::StringMatch(Handle<String> sub,
2736 Handle<String> pat,
2737 int start_index) {
2738 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002739 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002740
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002741 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002742 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002743
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002744 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002745 if (start_index + pattern_length > subject_length) return -1;
2746
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002747 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002748 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002749 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002750
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002751 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002752 // character patterns linear search is necessary, so any smart
2753 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002754 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002755 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002756 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002757 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002758 if (pchar > String::kMaxAsciiCharCode) {
2759 return -1;
2760 }
2761 Vector<const char> ascii_vector =
2762 sub->ToAsciiVector().SubVector(start_index, subject_length);
2763 const void* pos = memchr(ascii_vector.start(),
2764 static_cast<const char>(pchar),
2765 static_cast<size_t>(ascii_vector.length()));
2766 if (pos == NULL) {
2767 return -1;
2768 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002769 return static_cast<int>(reinterpret_cast<const char*>(pos)
2770 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002771 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002772 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002773 }
2774
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002775 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002776 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002777 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002778
ager@chromium.org7c537e22008-10-16 08:43:32 +00002779 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2780 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002781 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002782 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002783 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002784 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002785 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002786 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002787 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002788 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002789 if (sub->IsAsciiRepresentation()) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002790 return StringSearch(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002791 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002792 return StringSearch(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002793}
2794
2795
2796static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002797 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002798 ASSERT(args.length() == 3);
2799
ager@chromium.org7c537e22008-10-16 08:43:32 +00002800 CONVERT_ARG_CHECKED(String, sub, 0);
2801 CONVERT_ARG_CHECKED(String, pat, 1);
2802
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002803 Object* index = args[2];
2804 uint32_t start_index;
2805 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2806
ager@chromium.org870a0b62008-11-04 11:43:05 +00002807 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002808 int position = Runtime::StringMatch(sub, pat, start_index);
2809 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002810}
2811
2812
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002813template <typename schar, typename pchar>
2814static int StringMatchBackwards(Vector<const schar> sub,
2815 Vector<const pchar> pat,
2816 int idx) {
2817 ASSERT(pat.length() >= 1);
2818 ASSERT(idx + pat.length() <= sub.length());
2819
2820 if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
2821 for (int i = 0; i < pat.length(); i++) {
2822 uc16 c = pat[i];
2823 if (c > String::kMaxAsciiCharCode) {
2824 return -1;
2825 }
2826 }
2827 }
2828
2829 pchar pattern_first_char = pat[0];
2830 for (int i = idx; i >= 0; i--) {
2831 if (sub[i] != pattern_first_char) continue;
2832 int j = 1;
2833 while (j < pat.length()) {
2834 if (pat[j] != sub[i+j]) {
2835 break;
2836 }
2837 j++;
2838 }
2839 if (j == pat.length()) {
2840 return i;
2841 }
2842 }
2843 return -1;
2844}
2845
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002846static Object* Runtime_StringLastIndexOf(Arguments args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002847 HandleScope scope; // create a new handle scope
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002848 ASSERT(args.length() == 3);
2849
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002850 CONVERT_ARG_CHECKED(String, sub, 0);
2851 CONVERT_ARG_CHECKED(String, pat, 1);
2852
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002853 Object* index = args[2];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002854 uint32_t start_index;
2855 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2856
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002857 uint32_t pat_length = pat->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002858 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002859
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002860 if (start_index + pat_length > sub_length) {
2861 start_index = sub_length - pat_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002862 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002863
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002864 if (pat_length == 0) {
2865 return Smi::FromInt(start_index);
2866 }
2867
2868 if (!sub->IsFlat()) {
2869 FlattenString(sub);
2870 }
2871
2872 if (pat_length == 1) {
2873 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2874 if (sub->IsAsciiRepresentation()) {
2875 uc16 pchar = pat->Get(0);
2876 if (pchar > String::kMaxAsciiCharCode) {
2877 return Smi::FromInt(-1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002878 }
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002879 return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
2880 static_cast<char>(pat->Get(0)),
2881 start_index));
2882 } else {
2883 return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
2884 pat->Get(0),
2885 start_index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002886 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887 }
2888
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00002889 if (!pat->IsFlat()) {
2890 FlattenString(pat);
2891 }
2892
2893 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2894
2895 int position = -1;
2896
2897 if (pat->IsAsciiRepresentation()) {
2898 Vector<const char> pat_vector = pat->ToAsciiVector();
2899 if (sub->IsAsciiRepresentation()) {
2900 position = StringMatchBackwards(sub->ToAsciiVector(),
2901 pat_vector,
2902 start_index);
2903 } else {
2904 position = StringMatchBackwards(sub->ToUC16Vector(),
2905 pat_vector,
2906 start_index);
2907 }
2908 } else {
2909 Vector<const uc16> pat_vector = pat->ToUC16Vector();
2910 if (sub->IsAsciiRepresentation()) {
2911 position = StringMatchBackwards(sub->ToAsciiVector(),
2912 pat_vector,
2913 start_index);
2914 } else {
2915 position = StringMatchBackwards(sub->ToUC16Vector(),
2916 pat_vector,
2917 start_index);
2918 }
2919 }
2920
2921 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002922}
2923
2924
2925static Object* Runtime_StringLocaleCompare(Arguments args) {
2926 NoHandleAllocation ha;
2927 ASSERT(args.length() == 2);
2928
2929 CONVERT_CHECKED(String, str1, args[0]);
2930 CONVERT_CHECKED(String, str2, args[1]);
2931
2932 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002933 int str1_length = str1->length();
2934 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002935
2936 // Decide trivial cases without flattening.
2937 if (str1_length == 0) {
2938 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2939 return Smi::FromInt(-str2_length);
2940 } else {
2941 if (str2_length == 0) return Smi::FromInt(str1_length);
2942 }
2943
2944 int end = str1_length < str2_length ? str1_length : str2_length;
2945
2946 // No need to flatten if we are going to find the answer on the first
2947 // character. At this point we know there is at least one character
2948 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002949 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002950 if (d != 0) return Smi::FromInt(d);
2951
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002952 str1->TryFlatten();
2953 str2->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002954
2955 static StringInputBuffer buf1;
2956 static StringInputBuffer buf2;
2957
2958 buf1.Reset(str1);
2959 buf2.Reset(str2);
2960
2961 for (int i = 0; i < end; i++) {
2962 uint16_t char1 = buf1.GetNext();
2963 uint16_t char2 = buf2.GetNext();
2964 if (char1 != char2) return Smi::FromInt(char1 - char2);
2965 }
2966
2967 return Smi::FromInt(str1_length - str2_length);
2968}
2969
2970
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002971static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002972 NoHandleAllocation ha;
2973 ASSERT(args.length() == 3);
2974
2975 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002976 Object* from = args[1];
2977 Object* to = args[2];
2978 int start, end;
2979 // We have a fast integer-only case here to avoid a conversion to double in
2980 // the common case where from and to are Smis.
2981 if (from->IsSmi() && to->IsSmi()) {
2982 start = Smi::cast(from)->value();
2983 end = Smi::cast(to)->value();
2984 } else {
2985 CONVERT_DOUBLE_CHECKED(from_number, from);
2986 CONVERT_DOUBLE_CHECKED(to_number, to);
2987 start = FastD2I(from_number);
2988 end = FastD2I(to_number);
2989 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002990 RUNTIME_ASSERT(end >= start);
2991 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002992 RUNTIME_ASSERT(end <= value->length());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002993 Counters::sub_string_runtime.Increment();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002994 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002995}
2996
2997
ager@chromium.org41826e72009-03-30 13:30:57 +00002998static Object* Runtime_StringMatch(Arguments args) {
2999 ASSERT_EQ(3, args.length());
3000
3001 CONVERT_ARG_CHECKED(String, subject, 0);
3002 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
3003 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
3004 HandleScope handles;
3005
3006 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
3007
3008 if (match.is_null()) {
3009 return Failure::Exception();
3010 }
3011 if (match->IsNull()) {
3012 return Heap::null_value();
3013 }
3014 int length = subject->length();
3015
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00003016 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00003017 ZoneList<int> offsets(8);
3018 do {
3019 int start;
3020 int end;
3021 {
3022 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003023 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00003024 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
3025 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
3026 }
3027 offsets.Add(start);
3028 offsets.Add(end);
3029 int index = start < end ? end : end + 1;
3030 if (index > length) break;
3031 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
3032 if (match.is_null()) {
3033 return Failure::Exception();
3034 }
3035 } while (!match->IsNull());
3036 int matches = offsets.length() / 2;
3037 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
3038 for (int i = 0; i < matches ; i++) {
3039 int from = offsets.at(i * 2);
3040 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003041 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00003042 }
3043 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
3044 result->set_length(Smi::FromInt(matches));
3045 return *result;
3046}
3047
3048
lrn@chromium.org25156de2010-04-06 13:10:27 +00003049// Two smis before and after the match, for very long strings.
3050const int kMaxBuilderEntriesPerRegExpMatch = 5;
3051
3052
3053static void SetLastMatchInfoNoCaptures(Handle<String> subject,
3054 Handle<JSArray> last_match_info,
3055 int match_start,
3056 int match_end) {
3057 // Fill last_match_info with a single capture.
3058 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
3059 AssertNoAllocation no_gc;
3060 FixedArray* elements = FixedArray::cast(last_match_info->elements());
3061 RegExpImpl::SetLastCaptureCount(elements, 2);
3062 RegExpImpl::SetLastInput(elements, *subject);
3063 RegExpImpl::SetLastSubject(elements, *subject);
3064 RegExpImpl::SetCapture(elements, 0, match_start);
3065 RegExpImpl::SetCapture(elements, 1, match_end);
3066}
3067
3068
3069template <typename schar>
3070static bool SearchCharMultiple(Vector<schar> subject,
3071 String* pattern,
3072 schar pattern_char,
3073 FixedArrayBuilder* builder,
3074 int* match_pos) {
3075 // Position of last match.
3076 int pos = *match_pos;
3077 int subject_length = subject.length();
3078 while (pos < subject_length) {
3079 int match_end = pos + 1;
3080 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3081 *match_pos = pos;
3082 return false;
3083 }
3084 int new_pos = SingleCharIndexOf(subject, pattern_char, match_end);
3085 if (new_pos >= 0) {
3086 // Match has been found.
3087 if (new_pos > match_end) {
3088 ReplacementStringBuilder::AddSubjectSlice(builder, match_end, new_pos);
3089 }
3090 pos = new_pos;
3091 builder->Add(pattern);
3092 } else {
3093 break;
3094 }
3095 }
3096 if (pos + 1 < subject_length) {
3097 ReplacementStringBuilder::AddSubjectSlice(builder, pos + 1, subject_length);
3098 }
3099 *match_pos = pos;
3100 return true;
3101}
3102
3103
3104static bool SearchCharMultiple(Handle<String> subject,
3105 Handle<String> pattern,
3106 Handle<JSArray> last_match_info,
3107 FixedArrayBuilder* builder) {
3108 ASSERT(subject->IsFlat());
3109 ASSERT_EQ(1, pattern->length());
3110 uc16 pattern_char = pattern->Get(0);
3111 // Treating position before first as initial "previous match position".
3112 int match_pos = -1;
3113
3114 for (;;) { // Break when search complete.
3115 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3116 AssertNoAllocation no_gc;
3117 if (subject->IsAsciiRepresentation()) {
3118 if (pattern_char > String::kMaxAsciiCharCode) {
3119 break;
3120 }
3121 Vector<const char> subject_vector = subject->ToAsciiVector();
3122 char pattern_ascii_char = static_cast<char>(pattern_char);
3123 bool complete = SearchCharMultiple<const char>(subject_vector,
3124 *pattern,
3125 pattern_ascii_char,
3126 builder,
3127 &match_pos);
3128 if (complete) break;
3129 } else {
3130 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3131 bool complete = SearchCharMultiple<const uc16>(subject_vector,
3132 *pattern,
3133 pattern_char,
3134 builder,
3135 &match_pos);
3136 if (complete) break;
3137 }
3138 }
3139
3140 if (match_pos >= 0) {
3141 SetLastMatchInfoNoCaptures(subject,
3142 last_match_info,
3143 match_pos,
3144 match_pos + 1);
3145 return true;
3146 }
3147 return false; // No matches at all.
3148}
3149
3150
3151template <typename schar, typename pchar>
3152static bool SearchStringMultiple(Vector<schar> subject,
3153 String* pattern,
3154 Vector<pchar> pattern_string,
3155 FixedArrayBuilder* builder,
3156 int* match_pos) {
3157 int pos = *match_pos;
3158 int subject_length = subject.length();
3159 int pattern_length = pattern_string.length();
3160 int max_search_start = subject_length - pattern_length;
3161 bool is_ascii = (sizeof(schar) == 1);
3162 StringSearchStrategy strategy =
3163 InitializeStringSearch(pattern_string, is_ascii);
3164 switch (strategy) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003165 case SEARCH_FAIL: break;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003166 case SEARCH_SHORT:
3167 while (pos <= max_search_start) {
3168 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
3169 *match_pos = pos;
3170 return false;
3171 }
3172 // Position of end of previous match.
3173 int match_end = pos + pattern_length;
3174 int new_pos = SimpleIndexOf(subject, pattern_string, match_end);
3175 if (new_pos >= 0) {
3176 // A match.
3177 if (new_pos > match_end) {
3178 ReplacementStringBuilder::AddSubjectSlice(builder,
3179 match_end,
3180 new_pos);
3181 }
3182 pos = new_pos;
3183 builder->Add(pattern);
3184 } else {
3185 break;
3186 }
3187 }
3188 break;
3189 case SEARCH_LONG:
3190 while (pos <= max_search_start) {
3191 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003192 *match_pos = pos;
3193 return false;
lrn@chromium.org25156de2010-04-06 13:10:27 +00003194 }
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003195 int match_end = pos + pattern_length;
3196 int new_pos = ComplexIndexOf(subject, pattern_string, match_end);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003197 if (new_pos >= 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003198 // A match has been found.
3199 if (new_pos > match_end) {
3200 ReplacementStringBuilder::AddSubjectSlice(builder,
3201 match_end,
3202 new_pos);
lrn@chromium.org25156de2010-04-06 13:10:27 +00003203 }
3204 pos = new_pos;
3205 builder->Add(pattern);
3206 } else {
3207 break;
3208 }
3209 }
3210 break;
3211 }
3212 if (pos < max_search_start) {
3213 ReplacementStringBuilder::AddSubjectSlice(builder,
3214 pos + pattern_length,
3215 subject_length);
3216 }
3217 *match_pos = pos;
3218 return true;
3219}
3220
3221
3222static bool SearchStringMultiple(Handle<String> subject,
3223 Handle<String> pattern,
3224 Handle<JSArray> last_match_info,
3225 FixedArrayBuilder* builder) {
3226 ASSERT(subject->IsFlat());
3227 ASSERT(pattern->IsFlat());
3228 ASSERT(pattern->length() > 1);
3229
3230 // Treating as if a previous match was before first character.
3231 int match_pos = -pattern->length();
3232
3233 for (;;) { // Break when search complete.
3234 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3235 AssertNoAllocation no_gc;
3236 if (subject->IsAsciiRepresentation()) {
3237 Vector<const char> subject_vector = subject->ToAsciiVector();
3238 if (pattern->IsAsciiRepresentation()) {
3239 if (SearchStringMultiple(subject_vector,
3240 *pattern,
3241 pattern->ToAsciiVector(),
3242 builder,
3243 &match_pos)) break;
3244 } else {
3245 if (SearchStringMultiple(subject_vector,
3246 *pattern,
3247 pattern->ToUC16Vector(),
3248 builder,
3249 &match_pos)) break;
3250 }
3251 } else {
3252 Vector<const uc16> subject_vector = subject->ToUC16Vector();
3253 if (pattern->IsAsciiRepresentation()) {
3254 if (SearchStringMultiple(subject_vector,
3255 *pattern,
3256 pattern->ToAsciiVector(),
3257 builder,
3258 &match_pos)) break;
3259 } else {
3260 if (SearchStringMultiple(subject_vector,
3261 *pattern,
3262 pattern->ToUC16Vector(),
3263 builder,
3264 &match_pos)) break;
3265 }
3266 }
3267 }
3268
3269 if (match_pos >= 0) {
3270 SetLastMatchInfoNoCaptures(subject,
3271 last_match_info,
3272 match_pos,
3273 match_pos + pattern->length());
3274 return true;
3275 }
3276 return false; // No matches at all.
3277}
3278
3279
3280static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple(
3281 Handle<String> subject,
3282 Handle<JSRegExp> regexp,
3283 Handle<JSArray> last_match_array,
3284 FixedArrayBuilder* builder) {
3285 ASSERT(subject->IsFlat());
3286 int match_start = -1;
3287 int match_end = 0;
3288 int pos = 0;
3289 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3290 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3291
3292 OffsetsVector registers(required_registers);
3293 Vector<int> register_vector(registers.vector(), registers.length());
3294 int subject_length = subject->length();
3295
3296 for (;;) { // Break on failure, return on exception.
3297 RegExpImpl::IrregexpResult result =
3298 RegExpImpl::IrregexpExecOnce(regexp,
3299 subject,
3300 pos,
3301 register_vector);
3302 if (result == RegExpImpl::RE_SUCCESS) {
3303 match_start = register_vector[0];
3304 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3305 if (match_end < match_start) {
3306 ReplacementStringBuilder::AddSubjectSlice(builder,
3307 match_end,
3308 match_start);
3309 }
3310 match_end = register_vector[1];
3311 HandleScope loop_scope;
3312 builder->Add(*Factory::NewSubString(subject, match_start, match_end));
3313 if (match_start != match_end) {
3314 pos = match_end;
3315 } else {
3316 pos = match_end + 1;
3317 if (pos > subject_length) break;
3318 }
3319 } else if (result == RegExpImpl::RE_FAILURE) {
3320 break;
3321 } else {
3322 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3323 return result;
3324 }
3325 }
3326
3327 if (match_start >= 0) {
3328 if (match_end < subject_length) {
3329 ReplacementStringBuilder::AddSubjectSlice(builder,
3330 match_end,
3331 subject_length);
3332 }
3333 SetLastMatchInfoNoCaptures(subject,
3334 last_match_array,
3335 match_start,
3336 match_end);
3337 return RegExpImpl::RE_SUCCESS;
3338 } else {
3339 return RegExpImpl::RE_FAILURE; // No matches at all.
3340 }
3341}
3342
3343
3344static RegExpImpl::IrregexpResult SearchRegExpMultiple(
3345 Handle<String> subject,
3346 Handle<JSRegExp> regexp,
3347 Handle<JSArray> last_match_array,
3348 FixedArrayBuilder* builder) {
3349
3350 ASSERT(subject->IsFlat());
3351 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
3352 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION;
3353
3354 OffsetsVector registers(required_registers);
3355 Vector<int> register_vector(registers.vector(), registers.length());
3356
3357 RegExpImpl::IrregexpResult result =
3358 RegExpImpl::IrregexpExecOnce(regexp,
3359 subject,
3360 0,
3361 register_vector);
3362
3363 int capture_count = regexp->CaptureCount();
3364 int subject_length = subject->length();
3365
3366 // Position to search from.
3367 int pos = 0;
3368 // End of previous match. Differs from pos if match was empty.
3369 int match_end = 0;
3370 if (result == RegExpImpl::RE_SUCCESS) {
3371 // Need to keep a copy of the previous match for creating last_match_info
3372 // at the end, so we have two vectors that we swap between.
3373 OffsetsVector registers2(required_registers);
3374 Vector<int> prev_register_vector(registers2.vector(), registers2.length());
3375
3376 do {
3377 int match_start = register_vector[0];
3378 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
3379 if (match_end < match_start) {
3380 ReplacementStringBuilder::AddSubjectSlice(builder,
3381 match_end,
3382 match_start);
3383 }
3384 match_end = register_vector[1];
3385
3386 {
3387 // Avoid accumulating new handles inside loop.
3388 HandleScope temp_scope;
3389 // Arguments array to replace function is match, captures, index and
3390 // subject, i.e., 3 + capture count in total.
3391 Handle<FixedArray> elements = Factory::NewFixedArray(3 + capture_count);
3392 elements->set(0, *Factory::NewSubString(subject,
3393 match_start,
3394 match_end));
3395 for (int i = 1; i <= capture_count; i++) {
3396 int start = register_vector[i * 2];
3397 if (start >= 0) {
3398 int end = register_vector[i * 2 + 1];
3399 ASSERT(start <= end);
3400 Handle<String> substring = Factory::NewSubString(subject,
3401 start,
3402 end);
3403 elements->set(i, *substring);
3404 } else {
3405 ASSERT(register_vector[i * 2 + 1] < 0);
3406 elements->set(i, Heap::undefined_value());
3407 }
3408 }
3409 elements->set(capture_count + 1, Smi::FromInt(match_start));
3410 elements->set(capture_count + 2, *subject);
3411 builder->Add(*Factory::NewJSArrayWithElements(elements));
3412 }
3413 // Swap register vectors, so the last successful match is in
3414 // prev_register_vector.
3415 Vector<int> tmp = prev_register_vector;
3416 prev_register_vector = register_vector;
3417 register_vector = tmp;
3418
3419 if (match_end > match_start) {
3420 pos = match_end;
3421 } else {
3422 pos = match_end + 1;
3423 if (pos > subject_length) {
3424 break;
3425 }
3426 }
3427
3428 result = RegExpImpl::IrregexpExecOnce(regexp,
3429 subject,
3430 pos,
3431 register_vector);
3432 } while (result == RegExpImpl::RE_SUCCESS);
3433
3434 if (result != RegExpImpl::RE_EXCEPTION) {
3435 // Finished matching, with at least one match.
3436 if (match_end < subject_length) {
3437 ReplacementStringBuilder::AddSubjectSlice(builder,
3438 match_end,
3439 subject_length);
3440 }
3441
3442 int last_match_capture_count = (capture_count + 1) * 2;
3443 int last_match_array_size =
3444 last_match_capture_count + RegExpImpl::kLastMatchOverhead;
3445 last_match_array->EnsureSize(last_match_array_size);
3446 AssertNoAllocation no_gc;
3447 FixedArray* elements = FixedArray::cast(last_match_array->elements());
3448 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
3449 RegExpImpl::SetLastSubject(elements, *subject);
3450 RegExpImpl::SetLastInput(elements, *subject);
3451 for (int i = 0; i < last_match_capture_count; i++) {
3452 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]);
3453 }
3454 return RegExpImpl::RE_SUCCESS;
3455 }
3456 }
3457 // No matches at all, return failure or exception result directly.
3458 return result;
3459}
3460
3461
3462static Object* Runtime_RegExpExecMultiple(Arguments args) {
3463 ASSERT(args.length() == 4);
3464 HandleScope handles;
3465
3466 CONVERT_ARG_CHECKED(String, subject, 1);
3467 if (!subject->IsFlat()) { FlattenString(subject); }
3468 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
3469 CONVERT_ARG_CHECKED(JSArray, last_match_info, 2);
3470 CONVERT_ARG_CHECKED(JSArray, result_array, 3);
3471
3472 ASSERT(last_match_info->HasFastElements());
3473 ASSERT(regexp->GetFlags().is_global());
3474 Handle<FixedArray> result_elements;
3475 if (result_array->HasFastElements()) {
3476 result_elements =
3477 Handle<FixedArray>(FixedArray::cast(result_array->elements()));
3478 } else {
3479 result_elements = Factory::NewFixedArrayWithHoles(16);
3480 }
3481 FixedArrayBuilder builder(result_elements);
3482
3483 if (regexp->TypeTag() == JSRegExp::ATOM) {
3484 Handle<String> pattern(
3485 String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
3486 int pattern_length = pattern->length();
3487 if (pattern_length == 1) {
3488 if (SearchCharMultiple(subject, pattern, last_match_info, &builder)) {
3489 return *builder.ToJSArray(result_array);
3490 }
3491 return Heap::null_value();
3492 }
3493
3494 if (!pattern->IsFlat()) FlattenString(pattern);
3495 if (SearchStringMultiple(subject, pattern, last_match_info, &builder)) {
3496 return *builder.ToJSArray(result_array);
3497 }
3498 return Heap::null_value();
3499 }
3500
3501 ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
3502
3503 RegExpImpl::IrregexpResult result;
3504 if (regexp->CaptureCount() == 0) {
3505 result = SearchRegExpNoCaptureMultiple(subject,
3506 regexp,
3507 last_match_info,
3508 &builder);
3509 } else {
3510 result = SearchRegExpMultiple(subject, regexp, last_match_info, &builder);
3511 }
3512 if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
3513 if (result == RegExpImpl::RE_FAILURE) return Heap::null_value();
3514 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);
3515 return Failure::Exception();
3516}
3517
3518
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003519static Object* Runtime_NumberToRadixString(Arguments args) {
3520 NoHandleAllocation ha;
3521 ASSERT(args.length() == 2);
3522
ager@chromium.orgeadaf222009-06-16 09:43:10 +00003523 // Fast case where the result is a one character string.
3524 if (args[0]->IsSmi() && args[1]->IsSmi()) {
3525 int value = Smi::cast(args[0])->value();
3526 int radix = Smi::cast(args[1])->value();
3527 if (value >= 0 && value < radix) {
3528 RUNTIME_ASSERT(radix <= 36);
3529 // Character array used for conversion.
3530 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
3531 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
3532 }
3533 }
3534
3535 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003536 CONVERT_DOUBLE_CHECKED(value, args[0]);
3537 if (isnan(value)) {
3538 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3539 }
3540 if (isinf(value)) {
3541 if (value < 0) {
3542 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3543 }
3544 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3545 }
3546 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
3547 int radix = FastD2I(radix_number);
3548 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3549 char* str = DoubleToRadixCString(value, radix);
3550 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3551 DeleteArray(str);
3552 return result;
3553}
3554
3555
3556static Object* Runtime_NumberToFixed(Arguments args) {
3557 NoHandleAllocation ha;
3558 ASSERT(args.length() == 2);
3559
3560 CONVERT_DOUBLE_CHECKED(value, args[0]);
3561 if (isnan(value)) {
3562 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3563 }
3564 if (isinf(value)) {
3565 if (value < 0) {
3566 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3567 }
3568 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3569 }
3570 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3571 int f = FastD2I(f_number);
3572 RUNTIME_ASSERT(f >= 0);
3573 char* str = DoubleToFixedCString(value, f);
3574 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3575 DeleteArray(str);
3576 return res;
3577}
3578
3579
3580static Object* Runtime_NumberToExponential(Arguments args) {
3581 NoHandleAllocation ha;
3582 ASSERT(args.length() == 2);
3583
3584 CONVERT_DOUBLE_CHECKED(value, args[0]);
3585 if (isnan(value)) {
3586 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3587 }
3588 if (isinf(value)) {
3589 if (value < 0) {
3590 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3591 }
3592 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3593 }
3594 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3595 int f = FastD2I(f_number);
3596 RUNTIME_ASSERT(f >= -1 && f <= 20);
3597 char* str = DoubleToExponentialCString(value, f);
3598 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3599 DeleteArray(str);
3600 return res;
3601}
3602
3603
3604static Object* Runtime_NumberToPrecision(Arguments args) {
3605 NoHandleAllocation ha;
3606 ASSERT(args.length() == 2);
3607
3608 CONVERT_DOUBLE_CHECKED(value, args[0]);
3609 if (isnan(value)) {
3610 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
3611 }
3612 if (isinf(value)) {
3613 if (value < 0) {
3614 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
3615 }
3616 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
3617 }
3618 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
3619 int f = FastD2I(f_number);
3620 RUNTIME_ASSERT(f >= 1 && f <= 21);
3621 char* str = DoubleToPrecisionCString(value, f);
3622 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
3623 DeleteArray(str);
3624 return res;
3625}
3626
3627
3628// Returns a single character string where first character equals
3629// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003630static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003631 if (index < static_cast<uint32_t>(string->length())) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003632 string->TryFlatten();
ager@chromium.org870a0b62008-11-04 11:43:05 +00003633 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003634 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003635 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003636 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003637}
3638
3639
3640Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
3641 // Handle [] indexing on Strings
3642 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003643 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
3644 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003645 }
3646
3647 // Handle [] indexing on String objects
3648 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003649 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
3650 Handle<Object> result =
3651 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
3652 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003653 }
3654
3655 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003656 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003657 return prototype->GetElement(index);
3658 }
3659
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003660 return GetElement(object, index);
3661}
3662
3663
3664Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003665 return object->GetElement(index);
3666}
3667
3668
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003669Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
3670 HandleScope scope;
3671
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003672 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003673 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003674 Handle<Object> error =
3675 Factory::NewTypeError("non_object_property_load",
3676 HandleVector(args, 2));
3677 return Top::Throw(*error);
3678 }
3679
3680 // Check if the given key is an array index.
3681 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003682 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003683 return GetElementOrCharAt(object, index);
3684 }
3685
3686 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003687 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003688 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003689 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003690 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003691 bool has_pending_exception = false;
3692 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003693 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003694 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003695 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003696 }
3697
ager@chromium.org32912102009-01-16 10:38:43 +00003698 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003699 // the element if so.
3700 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003701 return GetElementOrCharAt(object, index);
3702 } else {
3703 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003704 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003705 }
3706}
3707
3708
3709static Object* Runtime_GetProperty(Arguments args) {
3710 NoHandleAllocation ha;
3711 ASSERT(args.length() == 2);
3712
3713 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003714 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003715
3716 return Runtime::GetObjectProperty(object, key);
3717}
3718
3719
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003720// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003721static Object* Runtime_KeyedGetProperty(Arguments args) {
3722 NoHandleAllocation ha;
3723 ASSERT(args.length() == 2);
3724
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003725 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00003726 // itself.
3727 //
3728 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00003729 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00003730 // global proxy object never has properties. This is the case
3731 // because the global proxy object forwards everything to its hidden
3732 // prototype including local lookups.
3733 //
3734 // Additionally, we need to make sure that we do not cache results
3735 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003736 if (args[0]->IsJSObject() &&
3737 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00003738 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003739 args[1]->IsString()) {
3740 JSObject* receiver = JSObject::cast(args[0]);
3741 String* key = String::cast(args[1]);
3742 if (receiver->HasFastProperties()) {
3743 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003744 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003745 int offset = KeyedLookupCache::Lookup(receiver_map, key);
3746 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003747 Object* value = receiver->FastPropertyAt(offset);
3748 return value->IsTheHole() ? Heap::undefined_value() : value;
3749 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003750 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003751 LookupResult result;
3752 receiver->LocalLookup(key, &result);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00003753 if (result.IsProperty() && result.type() == FIELD) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003754 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003755 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003756 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003757 }
3758 } else {
3759 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003760 StringDictionary* dictionary = receiver->property_dictionary();
3761 int entry = dictionary->FindEntry(key);
3762 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003763 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003764 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00003765 if (!receiver->IsGlobalObject()) return value;
3766 value = JSGlobalPropertyCell::cast(value)->value();
3767 if (!value->IsTheHole()) return value;
3768 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003769 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00003770 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003771 } else if (args[0]->IsString() && args[1]->IsSmi()) {
3772 // Fast case for string indexing using [] with a smi index.
3773 HandleScope scope;
3774 Handle<String> str = args.at<String>(0);
3775 int index = Smi::cast(args[1])->value();
3776 Handle<Object> result = GetCharAt(str, index);
3777 return *result;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003778 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003779
3780 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003781 return Runtime::GetObjectProperty(args.at<Object>(0),
3782 args.at<Object>(1));
3783}
3784
3785
ager@chromium.org5c838252010-02-19 08:53:10 +00003786static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
3787 ASSERT(args.length() == 5);
3788 HandleScope scope;
3789 CONVERT_ARG_CHECKED(JSObject, obj, 0);
3790 CONVERT_CHECKED(String, name, args[1]);
3791 CONVERT_CHECKED(Smi, flag_setter, args[2]);
3792 CONVERT_CHECKED(JSFunction, fun, args[3]);
3793 CONVERT_CHECKED(Smi, flag_attr, args[4]);
3794 int unchecked = flag_attr->value();
3795 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3796 RUNTIME_ASSERT(!obj->IsNull());
3797 LookupResult result;
3798 obj->LocalLookupRealNamedProperty(name, &result);
3799
3800 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3801 // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
3802 // delete it to avoid running into trouble in DefineAccessor, which
3803 // handles this incorrectly if the property is readonly (does nothing)
3804 if (result.IsProperty() &&
3805 (result.type() == FIELD || result.type() == NORMAL
3806 || result.type() == CONSTANT_FUNCTION)) {
3807 obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
3808 }
3809 return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
3810}
3811
3812static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
3813 ASSERT(args.length() == 4);
3814 HandleScope scope;
3815 CONVERT_ARG_CHECKED(JSObject, js_object, 0);
3816 CONVERT_ARG_CHECKED(String, name, 1);
3817 Handle<Object> obj_value = args.at<Object>(2);
3818
3819 CONVERT_CHECKED(Smi, flag, args[3]);
3820 int unchecked = flag->value();
3821 RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
3822
3823 LookupResult result;
3824 js_object->LocalLookupRealNamedProperty(*name, &result);
3825
3826 PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
3827
3828 // Take special care when attributes are different and there is already
3829 // a property. For simplicity we normalize the property which enables us
3830 // to not worry about changing the instance_descriptor and creating a new
3831 // map. The current version of SetObjectProperty does not handle attributes
3832 // correctly in the case where a property is a field and is reset with
3833 // new attributes.
3834 if (result.IsProperty() && attr != result.GetAttributes()) {
3835 // New attributes - normalize to avoid writing to instance descriptor
3836 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
3837 // Use IgnoreAttributes version since a readonly property may be
3838 // overridden and SetProperty does not allow this.
3839 return js_object->IgnoreAttributesAndSetLocalProperty(*name,
3840 *obj_value,
3841 attr);
3842 }
3843 return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
3844}
3845
3846
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847Object* Runtime::SetObjectProperty(Handle<Object> object,
3848 Handle<Object> key,
3849 Handle<Object> value,
3850 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003851 HandleScope scope;
3852
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003853 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003854 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003855 Handle<Object> error =
3856 Factory::NewTypeError("non_object_property_store",
3857 HandleVector(args, 2));
3858 return Top::Throw(*error);
3859 }
3860
3861 // If the object isn't a JavaScript object, we ignore the store.
3862 if (!object->IsJSObject()) return *value;
3863
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003864 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866 // Check if the given key is an array index.
3867 uint32_t index;
3868 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003869 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3870 // of a string using [] notation. We need to support this too in
3871 // JavaScript.
3872 // In the case of a String object we just need to redirect the assignment to
3873 // the underlying string if the index is in range. Since the underlying
3874 // string does nothing with the assignment then we can ignore such
3875 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003876 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003877 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003878 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003879
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003880 Handle<Object> result = SetElement(js_object, index, value);
3881 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003882 return *value;
3883 }
3884
3885 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003886 Handle<Object> result;
3887 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003888 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003889 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003890 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003891 key_string->TryFlatten();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003892 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003893 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003894 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 return *value;
3896 }
3897
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003898 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003899 bool has_pending_exception = false;
3900 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3901 if (has_pending_exception) return Failure::Exception();
3902 Handle<String> name = Handle<String>::cast(converted);
3903
3904 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003905 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003907 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003908 }
3909}
3910
3911
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003912Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
3913 Handle<Object> key,
3914 Handle<Object> value,
3915 PropertyAttributes attr) {
3916 HandleScope scope;
3917
3918 // Check if the given key is an array index.
3919 uint32_t index;
3920 if (Array::IndexFromObject(*key, &index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003921 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
3922 // of a string using [] notation. We need to support this too in
3923 // JavaScript.
3924 // In the case of a String object we just need to redirect the assignment to
3925 // the underlying string if the index is in range. Since the underlying
3926 // string does nothing with the assignment then we can ignore such
3927 // assignments.
3928 if (js_object->IsStringObjectWithCharacterAt(index)) {
3929 return *value;
3930 }
3931
3932 return js_object->SetElement(index, *value);
3933 }
3934
3935 if (key->IsString()) {
3936 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003937 return js_object->SetElement(index, *value);
3938 } else {
3939 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003940 key_string->TryFlatten();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003941 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
3942 *value,
3943 attr);
3944 }
3945 }
3946
3947 // Call-back into JavaScript to convert the key to a string.
3948 bool has_pending_exception = false;
3949 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3950 if (has_pending_exception) return Failure::Exception();
3951 Handle<String> name = Handle<String>::cast(converted);
3952
3953 if (name->AsArrayIndex(&index)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003954 return js_object->SetElement(index, *value);
3955 } else {
3956 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
3957 }
3958}
3959
3960
ager@chromium.orge2902be2009-06-08 12:21:35 +00003961Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
3962 Handle<Object> key) {
3963 HandleScope scope;
3964
3965 // Check if the given key is an array index.
3966 uint32_t index;
3967 if (Array::IndexFromObject(*key, &index)) {
3968 // In Firefox/SpiderMonkey, Safari and Opera you can access the
3969 // characters of a string using [] notation. In the case of a
3970 // String object we just need to redirect the deletion to the
3971 // underlying string if the index is in range. Since the
3972 // underlying string does nothing with the deletion, we can ignore
3973 // such deletions.
3974 if (js_object->IsStringObjectWithCharacterAt(index)) {
3975 return Heap::true_value();
3976 }
3977
3978 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
3979 }
3980
3981 Handle<String> key_string;
3982 if (key->IsString()) {
3983 key_string = Handle<String>::cast(key);
3984 } else {
3985 // Call-back into JavaScript to convert the key to a string.
3986 bool has_pending_exception = false;
3987 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
3988 if (has_pending_exception) return Failure::Exception();
3989 key_string = Handle<String>::cast(converted);
3990 }
3991
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003992 key_string->TryFlatten();
ager@chromium.orge2902be2009-06-08 12:21:35 +00003993 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
3994}
3995
3996
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003997static Object* Runtime_SetProperty(Arguments args) {
3998 NoHandleAllocation ha;
3999 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
4000
4001 Handle<Object> object = args.at<Object>(0);
4002 Handle<Object> key = args.at<Object>(1);
4003 Handle<Object> value = args.at<Object>(2);
4004
4005 // Compute attributes.
4006 PropertyAttributes attributes = NONE;
4007 if (args.length() == 4) {
4008 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004009 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004010 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004011 RUNTIME_ASSERT(
4012 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4013 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004014 }
4015 return Runtime::SetObjectProperty(object, key, value, attributes);
4016}
4017
4018
4019// Set a local property, even if it is READ_ONLY. If the property does not
4020// exist, it will be added with attributes NONE.
4021static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
4022 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004023 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004024 CONVERT_CHECKED(JSObject, object, args[0]);
4025 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004026 // Compute attributes.
4027 PropertyAttributes attributes = NONE;
4028 if (args.length() == 4) {
4029 CONVERT_CHECKED(Smi, value_obj, args[3]);
4030 int unchecked_value = value_obj->value();
4031 // Only attribute bits should be set.
4032 RUNTIME_ASSERT(
4033 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4034 attributes = static_cast<PropertyAttributes>(unchecked_value);
4035 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004036
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004037 return object->
4038 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004039}
4040
4041
4042static Object* Runtime_DeleteProperty(Arguments args) {
4043 NoHandleAllocation ha;
4044 ASSERT(args.length() == 2);
4045
4046 CONVERT_CHECKED(JSObject, object, args[0]);
4047 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00004048 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004049}
4050
4051
ager@chromium.org9085a012009-05-11 19:22:57 +00004052static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
4053 Handle<String> key) {
4054 if (object->HasLocalProperty(*key)) return Heap::true_value();
4055 // Handle hidden prototypes. If there's a hidden prototype above this thing
4056 // then we have to check it for properties, because they are supposed to
4057 // look like they are on this object.
4058 Handle<Object> proto(object->GetPrototype());
4059 if (proto->IsJSObject() &&
4060 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
4061 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
4062 }
4063 return Heap::false_value();
4064}
4065
4066
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004067static Object* Runtime_HasLocalProperty(Arguments args) {
4068 NoHandleAllocation ha;
4069 ASSERT(args.length() == 2);
4070 CONVERT_CHECKED(String, key, args[1]);
4071
ager@chromium.org9085a012009-05-11 19:22:57 +00004072 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004073 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00004074 if (obj->IsJSObject()) {
4075 JSObject* object = JSObject::cast(obj);
4076 // Fast case - no interceptors.
4077 if (object->HasRealNamedProperty(key)) return Heap::true_value();
4078 // Slow case. Either it's not there or we have an interceptor. We should
4079 // have handles for this kind of deal.
4080 HandleScope scope;
4081 return HasLocalPropertyImplementation(Handle<JSObject>(object),
4082 Handle<String>(key));
4083 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004084 // Well, there is one exception: Handle [] on strings.
4085 uint32_t index;
4086 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00004087 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00004088 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004089 return Heap::true_value();
4090 }
4091 }
4092 return Heap::false_value();
4093}
4094
4095
4096static Object* Runtime_HasProperty(Arguments args) {
4097 NoHandleAllocation na;
4098 ASSERT(args.length() == 2);
4099
4100 // Only JS objects can have properties.
4101 if (args[0]->IsJSObject()) {
4102 JSObject* object = JSObject::cast(args[0]);
4103 CONVERT_CHECKED(String, key, args[1]);
4104 if (object->HasProperty(key)) return Heap::true_value();
4105 }
4106 return Heap::false_value();
4107}
4108
4109
4110static Object* Runtime_HasElement(Arguments args) {
4111 NoHandleAllocation na;
4112 ASSERT(args.length() == 2);
4113
4114 // Only JS objects can have elements.
4115 if (args[0]->IsJSObject()) {
4116 JSObject* object = JSObject::cast(args[0]);
4117 CONVERT_CHECKED(Smi, index_obj, args[1]);
4118 uint32_t index = index_obj->value();
4119 if (object->HasElement(index)) return Heap::true_value();
4120 }
4121 return Heap::false_value();
4122}
4123
4124
4125static Object* Runtime_IsPropertyEnumerable(Arguments args) {
4126 NoHandleAllocation ha;
4127 ASSERT(args.length() == 2);
4128
4129 CONVERT_CHECKED(JSObject, object, args[0]);
4130 CONVERT_CHECKED(String, key, args[1]);
4131
4132 uint32_t index;
4133 if (key->AsArrayIndex(&index)) {
4134 return Heap::ToBoolean(object->HasElement(index));
4135 }
4136
ager@chromium.org870a0b62008-11-04 11:43:05 +00004137 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
4138 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004139}
4140
4141
4142static Object* Runtime_GetPropertyNames(Arguments args) {
4143 HandleScope scope;
4144 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004145 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004146 return *GetKeysFor(object);
4147}
4148
4149
4150// Returns either a FixedArray as Runtime_GetPropertyNames,
4151// or, if the given object has an enum cache that contains
4152// all enumerable properties of the object and its prototypes
4153// have none, the map of the object. This is used to speed up
4154// the check for deletions during a for-in.
4155static Object* Runtime_GetPropertyNamesFast(Arguments args) {
4156 ASSERT(args.length() == 1);
4157
4158 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4159
4160 if (raw_object->IsSimpleEnum()) return raw_object->map();
4161
4162 HandleScope scope;
4163 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004164 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
4165 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004166
4167 // Test again, since cache may have been built by preceding call.
4168 if (object->IsSimpleEnum()) return object->map();
4169
4170 return *content;
4171}
4172
4173
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004174// Find the length of the prototype chain that is to to handled as one. If a
4175// prototype object is hidden it is to be viewed as part of the the object it
4176// is prototype for.
4177static int LocalPrototypeChainLength(JSObject* obj) {
4178 int count = 1;
4179 Object* proto = obj->GetPrototype();
4180 while (proto->IsJSObject() &&
4181 JSObject::cast(proto)->map()->is_hidden_prototype()) {
4182 count++;
4183 proto = JSObject::cast(proto)->GetPrototype();
4184 }
4185 return count;
4186}
4187
4188
4189// Return the names of the local named properties.
4190// args[0]: object
4191static Object* Runtime_GetLocalPropertyNames(Arguments args) {
4192 HandleScope scope;
4193 ASSERT(args.length() == 1);
4194 if (!args[0]->IsJSObject()) {
4195 return Heap::undefined_value();
4196 }
4197 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4198
4199 // Skip the global proxy as it has no properties and always delegates to the
4200 // real global object.
4201 if (obj->IsJSGlobalProxy()) {
4202 // Only collect names if access is permitted.
4203 if (obj->IsAccessCheckNeeded() &&
4204 !Top::MayNamedAccess(*obj, Heap::undefined_value(), v8::ACCESS_KEYS)) {
4205 Top::ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
4206 return *Factory::NewJSArray(0);
4207 }
4208 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
4209 }
4210
4211 // Find the number of objects making up this.
4212 int length = LocalPrototypeChainLength(*obj);
4213
4214 // Find the number of local properties for each of the objects.
4215 int* local_property_count = NewArray<int>(length);
4216 int total_property_count = 0;
4217 Handle<JSObject> jsproto = obj;
4218 for (int i = 0; i < length; i++) {
4219 // Only collect names if access is permitted.
4220 if (jsproto->IsAccessCheckNeeded() &&
4221 !Top::MayNamedAccess(*jsproto,
4222 Heap::undefined_value(),
4223 v8::ACCESS_KEYS)) {
4224 Top::ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
4225 return *Factory::NewJSArray(0);
4226 }
4227 int n;
4228 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4229 local_property_count[i] = n;
4230 total_property_count += n;
4231 if (i < length - 1) {
4232 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4233 }
4234 }
4235
4236 // Allocate an array with storage for all the property names.
4237 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
4238
4239 // Get the property names.
4240 jsproto = obj;
4241 int proto_with_hidden_properties = 0;
4242 for (int i = 0; i < length; i++) {
4243 jsproto->GetLocalPropertyNames(*names,
4244 i == 0 ? 0 : local_property_count[i - 1]);
4245 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
4246 proto_with_hidden_properties++;
4247 }
4248 if (i < length - 1) {
4249 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
4250 }
4251 }
4252
4253 // Filter out name of hidden propeties object.
4254 if (proto_with_hidden_properties > 0) {
4255 Handle<FixedArray> old_names = names;
4256 names = Factory::NewFixedArray(
4257 names->length() - proto_with_hidden_properties);
4258 int dest_pos = 0;
4259 for (int i = 0; i < total_property_count; i++) {
4260 Object* name = old_names->get(i);
4261 if (name == Heap::hidden_symbol()) {
4262 continue;
4263 }
4264 names->set(dest_pos++, name);
4265 }
4266 }
4267
4268 DeleteArray(local_property_count);
4269 return *Factory::NewJSArrayWithElements(names);
4270}
4271
4272
4273// Return the names of the local indexed properties.
4274// args[0]: object
4275static Object* Runtime_GetLocalElementNames(Arguments args) {
4276 HandleScope scope;
4277 ASSERT(args.length() == 1);
4278 if (!args[0]->IsJSObject()) {
4279 return Heap::undefined_value();
4280 }
4281 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4282
4283 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4284 Handle<FixedArray> names = Factory::NewFixedArray(n);
4285 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4286 return *Factory::NewJSArrayWithElements(names);
4287}
4288
4289
4290// Return information on whether an object has a named or indexed interceptor.
4291// args[0]: object
4292static Object* Runtime_GetInterceptorInfo(Arguments args) {
4293 HandleScope scope;
4294 ASSERT(args.length() == 1);
4295 if (!args[0]->IsJSObject()) {
4296 return Smi::FromInt(0);
4297 }
4298 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4299
4300 int result = 0;
4301 if (obj->HasNamedInterceptor()) result |= 2;
4302 if (obj->HasIndexedInterceptor()) result |= 1;
4303
4304 return Smi::FromInt(result);
4305}
4306
4307
4308// Return property names from named interceptor.
4309// args[0]: object
4310static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) {
4311 HandleScope scope;
4312 ASSERT(args.length() == 1);
4313 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4314
4315 if (obj->HasNamedInterceptor()) {
4316 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4317 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4318 }
4319 return Heap::undefined_value();
4320}
4321
4322
4323// Return element names from indexed interceptor.
4324// args[0]: object
4325static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) {
4326 HandleScope scope;
4327 ASSERT(args.length() == 1);
4328 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4329
4330 if (obj->HasIndexedInterceptor()) {
4331 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4332 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4333 }
4334 return Heap::undefined_value();
4335}
4336
4337
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004338static Object* Runtime_LocalKeys(Arguments args) {
4339 ASSERT_EQ(args.length(), 1);
4340 CONVERT_CHECKED(JSObject, raw_object, args[0]);
4341 HandleScope scope;
4342 Handle<JSObject> object(raw_object);
4343 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
4344 LOCAL_ONLY);
4345 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
4346 // property array and since the result is mutable we have to create
4347 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00004348 int length = contents->length();
4349 Handle<FixedArray> copy = Factory::NewFixedArray(length);
4350 for (int i = 0; i < length; i++) {
4351 Object* entry = contents->get(i);
4352 if (entry->IsString()) {
4353 copy->set(i, entry);
4354 } else {
4355 ASSERT(entry->IsNumber());
4356 HandleScope scope;
4357 Handle<Object> entry_handle(entry);
4358 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
4359 copy->set(i, *entry_str);
4360 }
4361 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00004362 return *Factory::NewJSArrayWithElements(copy);
4363}
4364
4365
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004366static Object* Runtime_GetArgumentsProperty(Arguments args) {
4367 NoHandleAllocation ha;
4368 ASSERT(args.length() == 1);
4369
4370 // Compute the frame holding the arguments.
4371 JavaScriptFrameIterator it;
4372 it.AdvanceToArgumentsFrame();
4373 JavaScriptFrame* frame = it.frame();
4374
4375 // Get the actual number of provided arguments.
4376 const uint32_t n = frame->GetProvidedParametersCount();
4377
4378 // Try to convert the key to an index. If successful and within
4379 // index return the the argument from the frame.
4380 uint32_t index;
4381 if (Array::IndexFromObject(args[0], &index) && index < n) {
4382 return frame->GetParameter(index);
4383 }
4384
4385 // Convert the key to a string.
4386 HandleScope scope;
4387 bool exception = false;
4388 Handle<Object> converted =
4389 Execution::ToString(args.at<Object>(0), &exception);
4390 if (exception) return Failure::Exception();
4391 Handle<String> key = Handle<String>::cast(converted);
4392
4393 // Try to convert the string key into an array index.
4394 if (key->AsArrayIndex(&index)) {
4395 if (index < n) {
4396 return frame->GetParameter(index);
4397 } else {
4398 return Top::initial_object_prototype()->GetElement(index);
4399 }
4400 }
4401
4402 // Handle special arguments properties.
4403 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
4404 if (key->Equals(Heap::callee_symbol())) return frame->function();
4405
4406 // Lookup in the initial Object.prototype object.
4407 return Top::initial_object_prototype()->GetProperty(*key);
4408}
4409
4410
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004411static Object* Runtime_ToFastProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004412 HandleScope scope;
4413
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004414 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004415 Handle<Object> object = args.at<Object>(0);
4416 if (object->IsJSObject()) {
4417 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
ager@chromium.org5c838252010-02-19 08:53:10 +00004418 if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
4419 js_object->TransformToFastProperties(0);
4420 }
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004421 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004422 return *object;
4423}
4424
4425
4426static Object* Runtime_ToSlowProperties(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00004427 HandleScope scope;
4428
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004429 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004430 Handle<Object> object = args.at<Object>(0);
4431 if (object->IsJSObject()) {
4432 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004433 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00004434 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00004435 return *object;
4436}
4437
4438
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439static Object* Runtime_ToBool(Arguments args) {
4440 NoHandleAllocation ha;
4441 ASSERT(args.length() == 1);
4442
4443 return args[0]->ToBoolean();
4444}
4445
4446
4447// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
4448// Possible optimizations: put the type string into the oddballs.
4449static Object* Runtime_Typeof(Arguments args) {
4450 NoHandleAllocation ha;
4451
4452 Object* obj = args[0];
4453 if (obj->IsNumber()) return Heap::number_symbol();
4454 HeapObject* heap_obj = HeapObject::cast(obj);
4455
4456 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004457 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004458
4459 InstanceType instance_type = heap_obj->map()->instance_type();
4460 if (instance_type < FIRST_NONSTRING_TYPE) {
4461 return Heap::string_symbol();
4462 }
4463
4464 switch (instance_type) {
4465 case ODDBALL_TYPE:
4466 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
4467 return Heap::boolean_symbol();
4468 }
4469 if (heap_obj->IsNull()) {
4470 return Heap::object_symbol();
4471 }
4472 ASSERT(heap_obj->IsUndefined());
4473 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004474 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004475 return Heap::function_symbol();
4476 default:
4477 // For any kind of object not handled above, the spec rule for
4478 // host objects gives that it is okay to return "object"
4479 return Heap::object_symbol();
4480 }
4481}
4482
4483
lrn@chromium.org25156de2010-04-06 13:10:27 +00004484static bool AreDigits(const char*s, int from, int to) {
4485 for (int i = from; i < to; i++) {
4486 if (s[i] < '0' || s[i] > '9') return false;
4487 }
4488
4489 return true;
4490}
4491
4492
4493static int ParseDecimalInteger(const char*s, int from, int to) {
4494 ASSERT(to - from < 10); // Overflow is not possible.
4495 ASSERT(from < to);
4496 int d = s[from] - '0';
4497
4498 for (int i = from + 1; i < to; i++) {
4499 d = 10 * d + (s[i] - '0');
4500 }
4501
4502 return d;
4503}
4504
4505
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004506static Object* Runtime_StringToNumber(Arguments args) {
4507 NoHandleAllocation ha;
4508 ASSERT(args.length() == 1);
4509 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004510 subject->TryFlatten();
lrn@chromium.org25156de2010-04-06 13:10:27 +00004511
4512 // Fast case: short integer or some sorts of junk values.
4513 int len = subject->length();
4514 if (subject->IsSeqAsciiString()) {
4515 if (len == 0) return Smi::FromInt(0);
4516
4517 char const* data = SeqAsciiString::cast(subject)->GetChars();
4518 bool minus = (data[0] == '-');
4519 int start_pos = (minus ? 1 : 0);
4520
4521 if (start_pos == len) {
4522 return Heap::nan_value();
4523 } else if (data[start_pos] > '9') {
4524 // Fast check for a junk value. A valid string may start from a
4525 // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
4526 // the 'I' character ('Infinity'). All of that have codes not greater than
4527 // '9' except 'I'.
4528 if (data[start_pos] != 'I') {
4529 return Heap::nan_value();
4530 }
4531 } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
4532 // The maximal/minimal smi has 10 digits. If the string has less digits we
4533 // know it will fit into the smi-data type.
4534 int d = ParseDecimalInteger(data, start_pos, len);
4535 if (minus) {
4536 if (d == 0) return Heap::minus_zero_value();
4537 d = -d;
4538 }
4539 return Smi::FromInt(d);
4540 }
4541 }
4542
4543 // Slower case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004544 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
4545}
4546
4547
4548static Object* Runtime_StringFromCharCodeArray(Arguments args) {
4549 NoHandleAllocation ha;
4550 ASSERT(args.length() == 1);
4551
4552 CONVERT_CHECKED(JSArray, codes, args[0]);
4553 int length = Smi::cast(codes->length())->value();
4554
4555 // Check if the string can be ASCII.
4556 int i;
4557 for (i = 0; i < length; i++) {
4558 Object* element = codes->GetElement(i);
4559 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
4560 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
4561 break;
4562 }
4563
4564 Object* object = NULL;
4565 if (i == length) { // The string is ASCII.
4566 object = Heap::AllocateRawAsciiString(length);
4567 } else { // The string is not ASCII.
4568 object = Heap::AllocateRawTwoByteString(length);
4569 }
4570
4571 if (object->IsFailure()) return object;
4572 String* result = String::cast(object);
4573 for (int i = 0; i < length; i++) {
4574 Object* element = codes->GetElement(i);
4575 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004576 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004577 }
4578 return result;
4579}
4580
4581
4582// kNotEscaped is generated by the following:
4583//
4584// #!/bin/perl
4585// for (my $i = 0; $i < 256; $i++) {
4586// print "\n" if $i % 16 == 0;
4587// my $c = chr($i);
4588// my $escaped = 1;
4589// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
4590// print $escaped ? "0, " : "1, ";
4591// }
4592
4593
4594static bool IsNotEscaped(uint16_t character) {
4595 // Only for 8 bit characters, the rest are always escaped (in a different way)
4596 ASSERT(character < 256);
4597 static const char kNotEscaped[256] = {
4598 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4599 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4600 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
4601 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
4602 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4603 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
4604 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
4605 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
4606 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4607 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4608 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4609 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4610 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4611 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4612 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4613 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4614 };
4615 return kNotEscaped[character] != 0;
4616}
4617
4618
4619static Object* Runtime_URIEscape(Arguments args) {
4620 const char hex_chars[] = "0123456789ABCDEF";
4621 NoHandleAllocation ha;
4622 ASSERT(args.length() == 1);
4623 CONVERT_CHECKED(String, source, args[0]);
4624
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004625 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004626
4627 int escaped_length = 0;
4628 int length = source->length();
4629 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004630 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004631 buffer->Reset(source);
4632 while (buffer->has_more()) {
4633 uint16_t character = buffer->GetNext();
4634 if (character >= 256) {
4635 escaped_length += 6;
4636 } else if (IsNotEscaped(character)) {
4637 escaped_length++;
4638 } else {
4639 escaped_length += 3;
4640 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004641 // We don't allow strings that are longer than a maximal length.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004642 ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004643 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004644 Top::context()->mark_out_of_memory();
4645 return Failure::OutOfMemoryException();
4646 }
4647 }
4648 }
4649 // No length change implies no change. Return original string if no change.
4650 if (escaped_length == length) {
4651 return source;
4652 }
4653 Object* o = Heap::AllocateRawAsciiString(escaped_length);
4654 if (o->IsFailure()) return o;
4655 String* destination = String::cast(o);
4656 int dest_position = 0;
4657
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004658 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004659 buffer->Rewind();
4660 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00004661 uint16_t chr = buffer->GetNext();
4662 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004663 destination->Set(dest_position, '%');
4664 destination->Set(dest_position+1, 'u');
4665 destination->Set(dest_position+2, hex_chars[chr >> 12]);
4666 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
4667 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
4668 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004669 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00004670 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004671 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004672 dest_position++;
4673 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004674 destination->Set(dest_position, '%');
4675 destination->Set(dest_position+1, hex_chars[chr >> 4]);
4676 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004677 dest_position += 3;
4678 }
4679 }
4680 return destination;
4681}
4682
4683
4684static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
4685 static const signed char kHexValue['g'] = {
4686 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4687 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4688 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4689 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
4690 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4691 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
4692 -1, 10, 11, 12, 13, 14, 15 };
4693
4694 if (character1 > 'f') return -1;
4695 int hi = kHexValue[character1];
4696 if (hi == -1) return -1;
4697 if (character2 > 'f') return -1;
4698 int lo = kHexValue[character2];
4699 if (lo == -1) return -1;
4700 return (hi << 4) + lo;
4701}
4702
4703
ager@chromium.org870a0b62008-11-04 11:43:05 +00004704static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00004705 int i,
4706 int length,
4707 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004708 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00004709 int32_t hi = 0;
4710 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004711 if (character == '%' &&
4712 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004713 source->Get(i + 1) == 'u' &&
4714 (hi = TwoDigitHex(source->Get(i + 2),
4715 source->Get(i + 3))) != -1 &&
4716 (lo = TwoDigitHex(source->Get(i + 4),
4717 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004718 *step = 6;
4719 return (hi << 8) + lo;
4720 } else if (character == '%' &&
4721 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004722 (lo = TwoDigitHex(source->Get(i + 1),
4723 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004724 *step = 3;
4725 return lo;
4726 } else {
4727 *step = 1;
4728 return character;
4729 }
4730}
4731
4732
4733static Object* Runtime_URIUnescape(Arguments args) {
4734 NoHandleAllocation ha;
4735 ASSERT(args.length() == 1);
4736 CONVERT_CHECKED(String, source, args[0]);
4737
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004738 source->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004739
4740 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004741 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004742
4743 int unescaped_length = 0;
4744 for (int i = 0; i < length; unescaped_length++) {
4745 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004746 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004747 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004748 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004749 i += step;
4750 }
4751
4752 // No length change implies no change. Return original string if no change.
4753 if (unescaped_length == length)
4754 return source;
4755
4756 Object* o = ascii ?
4757 Heap::AllocateRawAsciiString(unescaped_length) :
4758 Heap::AllocateRawTwoByteString(unescaped_length);
4759 if (o->IsFailure()) return o;
4760 String* destination = String::cast(o);
4761
4762 int dest_position = 0;
4763 for (int i = 0; i < length; dest_position++) {
4764 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004765 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004766 i += step;
4767 }
4768 return destination;
4769}
4770
4771
4772static Object* Runtime_StringParseInt(Arguments args) {
4773 NoHandleAllocation ha;
4774
4775 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004776 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004778 s->TryFlatten();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004779
lrn@chromium.org25156de2010-04-06 13:10:27 +00004780 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
4781 double value = StringToInt(s, radix);
4782 return Heap::NumberFromDouble(value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004783 return Heap::nan_value();
4784}
4785
4786
4787static Object* Runtime_StringParseFloat(Arguments args) {
4788 NoHandleAllocation ha;
4789 CONVERT_CHECKED(String, str, args[0]);
4790
4791 // ECMA-262 section 15.1.2.3, empty string is NaN
4792 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
4793
4794 // Create a number object from the value.
4795 return Heap::NumberFromDouble(value);
4796}
4797
4798
4799static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
4800static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
4801
4802
4803template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004804static Object* ConvertCaseHelper(String* s,
4805 int length,
4806 int input_string_length,
4807 unibrow::Mapping<Converter, 128>* mapping) {
4808 // We try this twice, once with the assumption that the result is no longer
4809 // than the input and, if that assumption breaks, again with the exact
4810 // length. This may not be pretty, but it is nicer than what was here before
4811 // and I hereby claim my vaffel-is.
4812 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004813 // Allocate the resulting string.
4814 //
4815 // NOTE: This assumes that the upper/lower case of an ascii
4816 // character is also ascii. This is currently the case, but it
4817 // might break in the future if we implement more context and locale
4818 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00004819 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820 ? Heap::AllocateRawAsciiString(length)
4821 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004822 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004823 String* result = String::cast(o);
4824 bool has_changed_character = false;
4825
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004826 // Convert all characters to upper case, assuming that they will fit
4827 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004828 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004829 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004830 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004831 // We can assume that the string is not empty
4832 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004833 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00004834 bool has_next = buffer->has_more();
4835 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004836 int char_length = mapping->get(current, next, chars);
4837 if (char_length == 0) {
4838 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004839 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004840 i++;
4841 } else if (char_length == 1) {
4842 // Common case: converting the letter resulted in one character.
4843 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004844 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004845 has_changed_character = true;
4846 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004847 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004848 // We've assumed that the result would be as long as the
4849 // input but here is a character that converts to several
4850 // characters. No matter, we calculate the exact length
4851 // of the result and try the whole thing again.
4852 //
4853 // Note that this leaves room for optimization. We could just
4854 // memcpy what we already have to the result string. Also,
4855 // the result string is the last object allocated we could
4856 // "realloc" it and probably, in the vast majority of cases,
4857 // extend the existing string to be able to hold the full
4858 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00004859 int next_length = 0;
4860 if (has_next) {
4861 next_length = mapping->get(next, 0, chars);
4862 if (next_length == 0) next_length = 1;
4863 }
4864 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004865 while (buffer->has_more()) {
4866 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00004867 // NOTE: we use 0 as the next character here because, while
4868 // the next character may affect what a character converts to,
4869 // it does not in any case affect the length of what it convert
4870 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004871 int char_length = mapping->get(current, 0, chars);
4872 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00004873 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004874 if (current_length > Smi::kMaxValue) {
4875 Top::context()->mark_out_of_memory();
4876 return Failure::OutOfMemoryException();
4877 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004878 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004879 // Try again with the real length.
4880 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004881 } else {
4882 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004883 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004884 i++;
4885 }
4886 has_changed_character = true;
4887 }
4888 current = next;
4889 }
4890 if (has_changed_character) {
4891 return result;
4892 } else {
4893 // If we didn't actually change anything in doing the conversion
4894 // we simple return the result and let the converted string
4895 // become garbage; there is no reason to keep two identical strings
4896 // alive.
4897 return s;
4898 }
4899}
4900
4901
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004902static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
4903 if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
4904 if (s->IsConsString()) {
4905 ASSERT(ConsString::cast(s)->second()->length() == 0);
4906 return SeqAsciiString::cast(ConsString::cast(s)->first());
4907 }
4908 return SeqAsciiString::cast(s);
4909}
4910
4911
4912namespace {
4913
4914struct ToLowerTraits {
4915 typedef unibrow::ToLowercase UnibrowConverter;
4916
4917 static bool ConvertAscii(char* dst, char* src, int length) {
4918 bool changed = false;
4919 for (int i = 0; i < length; ++i) {
4920 char c = src[i];
4921 if ('A' <= c && c <= 'Z') {
4922 c += ('a' - 'A');
4923 changed = true;
4924 }
4925 dst[i] = c;
4926 }
4927 return changed;
4928 }
4929};
4930
4931
4932struct ToUpperTraits {
4933 typedef unibrow::ToUppercase UnibrowConverter;
4934
4935 static bool ConvertAscii(char* dst, char* src, int length) {
4936 bool changed = false;
4937 for (int i = 0; i < length; ++i) {
4938 char c = src[i];
4939 if ('a' <= c && c <= 'z') {
4940 c -= ('a' - 'A');
4941 changed = true;
4942 }
4943 dst[i] = c;
4944 }
4945 return changed;
4946 }
4947};
4948
4949} // namespace
4950
4951
4952template <typename ConvertTraits>
4953static Object* ConvertCase(
4954 Arguments args,
4955 unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004956 NoHandleAllocation ha;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004957 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004958 s->TryFlatten();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004959
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004960 const int length = s->length();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004961 // Assume that the string is not empty; we need this assumption later
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004962 if (length == 0) return s;
4963
4964 // Simpler handling of ascii strings.
4965 //
4966 // NOTE: This assumes that the upper/lower case of an ascii
4967 // character is also ascii. This is currently the case, but it
4968 // might break in the future if we implement more context and locale
4969 // dependent upper/lower conversions.
4970 SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
4971 if (seq_ascii != NULL) {
4972 Object* o = Heap::AllocateRawAsciiString(length);
4973 if (o->IsFailure()) return o;
4974 SeqAsciiString* result = SeqAsciiString::cast(o);
4975 bool has_changed_character = ConvertTraits::ConvertAscii(
4976 result->GetChars(), seq_ascii->GetChars(), length);
4977 return has_changed_character ? result : s;
4978 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004979
4980 Object* answer = ConvertCaseHelper(s, length, length, mapping);
4981 if (answer->IsSmi()) {
4982 // Retry with correct length.
4983 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
4984 }
4985 return answer; // This may be a failure.
4986}
4987
4988
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004989static Object* Runtime_StringToLowerCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004990 return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004991}
4992
4993
4994static Object* Runtime_StringToUpperCase(Arguments args) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004995 return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004996}
4997
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004998
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00004999static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
5000 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
5001}
5002
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005003
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005004static Object* Runtime_StringTrim(Arguments args) {
5005 NoHandleAllocation ha;
5006 ASSERT(args.length() == 3);
5007
5008 CONVERT_CHECKED(String, s, args[0]);
5009 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
5010 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
5011
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005012 s->TryFlatten();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005013 int length = s->length();
5014
5015 int left = 0;
5016 if (trimLeft) {
5017 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
5018 left++;
5019 }
5020 }
5021
5022 int right = length;
5023 if (trimRight) {
5024 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
5025 right--;
5026 }
5027 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005028 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005029}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005030
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005031
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005032template <typename schar, typename pchar>
5033void FindStringIndices(Vector<const schar> subject,
5034 Vector<const pchar> pattern,
5035 ZoneList<int>* indices,
5036 unsigned int limit) {
5037 ASSERT(limit > 0);
5038 // Collect indices of pattern in subject, and the end-of-string index.
5039 // Stop after finding at most limit values.
5040 StringSearchStrategy strategy =
5041 InitializeStringSearch(pattern, sizeof(schar) == 1);
5042 switch (strategy) {
5043 case SEARCH_FAIL: return;
5044 case SEARCH_SHORT: {
5045 int pattern_length = pattern.length();
5046 int index = 0;
5047 while (limit > 0) {
5048 index = SimpleIndexOf(subject, pattern, index);
5049 if (index < 0) return;
5050 indices->Add(index);
5051 index += pattern_length;
5052 limit--;
5053 }
5054 return;
5055 }
5056 case SEARCH_LONG: {
5057 int pattern_length = pattern.length();
5058 int index = 0;
5059 while (limit > 0) {
5060 index = ComplexIndexOf(subject, pattern, index);
5061 if (index < 0) return;
5062 indices->Add(index);
5063 index += pattern_length;
5064 limit--;
5065 }
5066 return;
5067 }
5068 default:
5069 UNREACHABLE();
5070 return;
5071 }
5072}
5073
5074template <typename schar>
5075inline void FindCharIndices(Vector<const schar> subject,
5076 const schar pattern_char,
5077 ZoneList<int>* indices,
5078 unsigned int limit) {
5079 // Collect indices of pattern_char in subject, and the end-of-string index.
5080 // Stop after finding at most limit values.
5081 int index = 0;
5082 while (limit > 0) {
5083 index = SingleCharIndexOf(subject, pattern_char, index);
5084 if (index < 0) return;
5085 indices->Add(index);
5086 index++;
5087 limit--;
5088 }
5089}
5090
5091
5092static Object* Runtime_StringSplit(Arguments args) {
5093 ASSERT(args.length() == 3);
5094 HandleScope handle_scope;
5095 CONVERT_ARG_CHECKED(String, subject, 0);
5096 CONVERT_ARG_CHECKED(String, pattern, 1);
5097 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
5098
5099 int subject_length = subject->length();
5100 int pattern_length = pattern->length();
5101 RUNTIME_ASSERT(pattern_length > 0);
5102
5103 // The limit can be very large (0xffffffffu), but since the pattern
5104 // isn't empty, we can never create more parts than ~half the length
5105 // of the subject.
5106
5107 if (!subject->IsFlat()) FlattenString(subject);
5108
5109 static const int kMaxInitialListCapacity = 16;
5110
5111 ZoneScope scope(DELETE_ON_EXIT);
5112
5113 // Find (up to limit) indices of separator and end-of-string in subject
5114 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
5115 ZoneList<int> indices(initial_capacity);
5116 if (pattern_length == 1) {
5117 // Special case, go directly to fast single-character split.
5118 AssertNoAllocation nogc;
5119 uc16 pattern_char = pattern->Get(0);
5120 if (subject->IsTwoByteRepresentation()) {
5121 FindCharIndices(subject->ToUC16Vector(), pattern_char,
5122 &indices,
5123 limit);
5124 } else if (pattern_char <= String::kMaxAsciiCharCode) {
5125 FindCharIndices(subject->ToAsciiVector(),
5126 static_cast<char>(pattern_char),
5127 &indices,
5128 limit);
5129 }
5130 } else {
5131 if (!pattern->IsFlat()) FlattenString(pattern);
5132 AssertNoAllocation nogc;
5133 if (subject->IsAsciiRepresentation()) {
5134 Vector<const char> subject_vector = subject->ToAsciiVector();
5135 if (pattern->IsAsciiRepresentation()) {
5136 FindStringIndices(subject_vector,
5137 pattern->ToAsciiVector(),
5138 &indices,
5139 limit);
5140 } else {
5141 FindStringIndices(subject_vector,
5142 pattern->ToUC16Vector(),
5143 &indices,
5144 limit);
5145 }
5146 } else {
5147 Vector<const uc16> subject_vector = subject->ToUC16Vector();
5148 if (pattern->IsAsciiRepresentation()) {
5149 FindStringIndices(subject_vector,
5150 pattern->ToAsciiVector(),
5151 &indices,
5152 limit);
5153 } else {
5154 FindStringIndices(subject_vector,
5155 pattern->ToUC16Vector(),
5156 &indices,
5157 limit);
5158 }
5159 }
5160 }
5161 if (static_cast<uint32_t>(indices.length()) < limit) {
5162 indices.Add(subject_length);
5163 }
5164 // The list indices now contains the end of each part to create.
5165
5166
5167 // Create JSArray of substrings separated by separator.
5168 int part_count = indices.length();
5169
5170 Handle<JSArray> result = Factory::NewJSArray(part_count);
5171 result->set_length(Smi::FromInt(part_count));
5172
5173 ASSERT(result->HasFastElements());
5174
5175 if (part_count == 1 && indices.at(0) == subject_length) {
5176 FixedArray::cast(result->elements())->set(0, *subject);
5177 return *result;
5178 }
5179
5180 Handle<FixedArray> elements(FixedArray::cast(result->elements()));
5181 int part_start = 0;
5182 for (int i = 0; i < part_count; i++) {
5183 HandleScope local_loop_handle;
5184 int part_end = indices.at(i);
5185 Handle<String> substring =
5186 Factory::NewSubString(subject, part_start, part_end);
5187 elements->set(i, *substring);
5188 part_start = part_end + pattern_length;
5189 }
5190
5191 return *result;
5192}
5193
5194
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005195// Copies ascii characters to the given fixed array looking up
5196// one-char strings in the cache. Gives up on the first char that is
5197// not in the cache and fills the remainder with smi zeros. Returns
5198// the length of the successfully copied prefix.
5199static int CopyCachedAsciiCharsToArray(const char* chars,
5200 FixedArray* elements,
5201 int length) {
5202 AssertNoAllocation nogc;
5203 FixedArray* ascii_cache = Heap::single_character_string_cache();
5204 Object* undefined = Heap::undefined_value();
5205 int i;
5206 for (i = 0; i < length; ++i) {
5207 Object* value = ascii_cache->get(chars[i]);
5208 if (value == undefined) break;
5209 ASSERT(!Heap::InNewSpace(value));
5210 elements->set(i, value, SKIP_WRITE_BARRIER);
5211 }
5212 if (i < length) {
5213 ASSERT(Smi::FromInt(0) == 0);
5214 memset(elements->data_start() + i, 0, kPointerSize * (length - i));
5215 }
5216#ifdef DEBUG
5217 for (int j = 0; j < length; ++j) {
5218 Object* element = elements->get(j);
5219 ASSERT(element == Smi::FromInt(0) ||
5220 (element->IsString() && String::cast(element)->LooksValid()));
5221 }
5222#endif
5223 return i;
5224}
5225
5226
5227// Converts a String to JSArray.
5228// For example, "foo" => ["f", "o", "o"].
5229static Object* Runtime_StringToArray(Arguments args) {
5230 HandleScope scope;
5231 ASSERT(args.length() == 1);
5232 CONVERT_ARG_CHECKED(String, s, 0);
5233
5234 s->TryFlatten();
5235 const int length = s->length();
5236
5237 Handle<FixedArray> elements;
5238 if (s->IsFlat() && s->IsAsciiRepresentation()) {
5239 Object* obj = Heap::AllocateUninitializedFixedArray(length);
5240 if (obj->IsFailure()) return obj;
5241 elements = Handle<FixedArray>(FixedArray::cast(obj));
5242
5243 Vector<const char> chars = s->ToAsciiVector();
5244 // Note, this will initialize all elements (not only the prefix)
5245 // to prevent GC from seeing partially initialized array.
5246 int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
5247 *elements,
5248 length);
5249
5250 for (int i = num_copied_from_cache; i < length; ++i) {
5251 elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
5252 }
5253 } else {
5254 elements = Factory::NewFixedArray(length);
5255 for (int i = 0; i < length; ++i) {
5256 elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
5257 }
5258 }
5259
5260#ifdef DEBUG
5261 for (int i = 0; i < length; ++i) {
5262 ASSERT(String::cast(elements->get(i))->length() == 1);
5263 }
5264#endif
5265
5266 return *Factory::NewJSArrayWithElements(elements);
5267}
5268
5269
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00005270bool Runtime::IsUpperCaseChar(uint16_t ch) {
5271 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
5272 int char_length = to_upper_mapping.get(ch, 0, chars);
5273 return char_length == 0;
5274}
5275
5276
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005277static Object* Runtime_NumberToString(Arguments args) {
5278 NoHandleAllocation ha;
5279 ASSERT(args.length() == 1);
5280
5281 Object* number = args[0];
5282 RUNTIME_ASSERT(number->IsNumber());
5283
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00005284 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005285}
5286
5287
ager@chromium.org357bf652010-04-12 11:30:10 +00005288static Object* Runtime_NumberToStringSkipCache(Arguments args) {
5289 NoHandleAllocation ha;
5290 ASSERT(args.length() == 1);
5291
5292 Object* number = args[0];
5293 RUNTIME_ASSERT(number->IsNumber());
5294
5295 return Heap::NumberToString(number, false);
5296}
5297
5298
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005299static Object* Runtime_NumberToInteger(Arguments args) {
5300 NoHandleAllocation ha;
5301 ASSERT(args.length() == 1);
5302
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005303 CONVERT_DOUBLE_CHECKED(number, args[0]);
5304
5305 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5306 if (number > 0 && number <= Smi::kMaxValue) {
5307 return Smi::FromInt(static_cast<int>(number));
5308 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005309 return Heap::NumberFromDouble(DoubleToInteger(number));
5310}
5311
5312
5313static Object* Runtime_NumberToJSUint32(Arguments args) {
5314 NoHandleAllocation ha;
5315 ASSERT(args.length() == 1);
5316
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005317 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005318 return Heap::NumberFromUint32(number);
5319}
5320
5321
5322static Object* Runtime_NumberToJSInt32(Arguments args) {
5323 NoHandleAllocation ha;
5324 ASSERT(args.length() == 1);
5325
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005326 CONVERT_DOUBLE_CHECKED(number, args[0]);
5327
5328 // We do not include 0 so that we don't have to treat +0 / -0 cases.
5329 if (number > 0 && number <= Smi::kMaxValue) {
5330 return Smi::FromInt(static_cast<int>(number));
5331 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005332 return Heap::NumberFromInt32(DoubleToInt32(number));
5333}
5334
5335
ager@chromium.org870a0b62008-11-04 11:43:05 +00005336// Converts a Number to a Smi, if possible. Returns NaN if the number is not
5337// a small integer.
5338static Object* Runtime_NumberToSmi(Arguments args) {
5339 NoHandleAllocation ha;
5340 ASSERT(args.length() == 1);
5341
5342 Object* obj = args[0];
5343 if (obj->IsSmi()) {
5344 return obj;
5345 }
5346 if (obj->IsHeapNumber()) {
5347 double value = HeapNumber::cast(obj)->value();
5348 int int_value = FastD2I(value);
5349 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
5350 return Smi::FromInt(int_value);
5351 }
5352 }
5353 return Heap::nan_value();
5354}
5355
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005356
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005357static Object* Runtime_NumberAdd(Arguments args) {
5358 NoHandleAllocation ha;
5359 ASSERT(args.length() == 2);
5360
5361 CONVERT_DOUBLE_CHECKED(x, args[0]);
5362 CONVERT_DOUBLE_CHECKED(y, args[1]);
5363 return Heap::AllocateHeapNumber(x + y);
5364}
5365
5366
5367static Object* Runtime_NumberSub(Arguments args) {
5368 NoHandleAllocation ha;
5369 ASSERT(args.length() == 2);
5370
5371 CONVERT_DOUBLE_CHECKED(x, args[0]);
5372 CONVERT_DOUBLE_CHECKED(y, args[1]);
5373 return Heap::AllocateHeapNumber(x - y);
5374}
5375
5376
5377static Object* Runtime_NumberMul(Arguments args) {
5378 NoHandleAllocation ha;
5379 ASSERT(args.length() == 2);
5380
5381 CONVERT_DOUBLE_CHECKED(x, args[0]);
5382 CONVERT_DOUBLE_CHECKED(y, args[1]);
5383 return Heap::AllocateHeapNumber(x * y);
5384}
5385
5386
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005387static Object* Runtime_NumberUnaryMinus(Arguments args) {
5388 NoHandleAllocation ha;
5389 ASSERT(args.length() == 1);
5390
5391 CONVERT_DOUBLE_CHECKED(x, args[0]);
5392 return Heap::AllocateHeapNumber(-x);
5393}
5394
5395
5396static Object* Runtime_NumberDiv(Arguments args) {
5397 NoHandleAllocation ha;
5398 ASSERT(args.length() == 2);
5399
5400 CONVERT_DOUBLE_CHECKED(x, args[0]);
5401 CONVERT_DOUBLE_CHECKED(y, args[1]);
5402 return Heap::NewNumberFromDouble(x / y);
5403}
5404
5405
5406static Object* Runtime_NumberMod(Arguments args) {
5407 NoHandleAllocation ha;
5408 ASSERT(args.length() == 2);
5409
5410 CONVERT_DOUBLE_CHECKED(x, args[0]);
5411 CONVERT_DOUBLE_CHECKED(y, args[1]);
5412
ager@chromium.org3811b432009-10-28 14:53:37 +00005413 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005414 // NewNumberFromDouble may return a Smi instead of a Number object
5415 return Heap::NewNumberFromDouble(x);
5416}
5417
5418
5419static Object* Runtime_StringAdd(Arguments args) {
5420 NoHandleAllocation ha;
5421 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005422 CONVERT_CHECKED(String, str1, args[0]);
5423 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005424 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00005425 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005426}
5427
5428
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005429template <typename sinkchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005430static inline void StringBuilderConcatHelper(String* special,
5431 sinkchar* sink,
5432 FixedArray* fixed_array,
5433 int array_length) {
5434 int position = 0;
5435 for (int i = 0; i < array_length; i++) {
5436 Object* element = fixed_array->get(i);
5437 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005438 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005439 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005440 int pos;
5441 int len;
5442 if (encoded_slice > 0) {
5443 // Position and length encoded in one smi.
5444 pos = StringBuilderSubstringPosition::decode(encoded_slice);
5445 len = StringBuilderSubstringLength::decode(encoded_slice);
5446 } else {
5447 // Position and length encoded in two smis.
5448 Object* obj = fixed_array->get(++i);
5449 ASSERT(obj->IsSmi());
5450 pos = Smi::cast(obj)->value();
5451 len = -encoded_slice;
5452 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00005453 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00005454 sink + position,
5455 pos,
5456 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005457 position += len;
5458 } else {
5459 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005460 int element_length = string->length();
5461 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005462 position += element_length;
5463 }
5464 }
5465}
5466
5467
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005468static Object* Runtime_StringBuilderConcat(Arguments args) {
5469 NoHandleAllocation ha;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005470 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005471 CONVERT_CHECKED(JSArray, array, args[0]);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005472 if (!args[1]->IsSmi()) {
5473 Top::context()->mark_out_of_memory();
5474 return Failure::OutOfMemoryException();
5475 }
5476 int array_length = Smi::cast(args[1])->value();
5477 CONVERT_CHECKED(String, special, args[2]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005478
5479 // This assumption is used by the slice encoding in one or two smis.
5480 ASSERT(Smi::kMaxValue >= String::kMaxLength);
5481
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005482 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005483 if (!array->HasFastElements()) {
5484 return Top::Throw(Heap::illegal_argument_symbol());
5485 }
5486 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005487 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005488 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005489 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005490
5491 if (array_length == 0) {
5492 return Heap::empty_string();
5493 } else if (array_length == 1) {
5494 Object* first = fixed_array->get(0);
5495 if (first->IsString()) return first;
5496 }
5497
ager@chromium.org5ec48922009-05-05 07:25:34 +00005498 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005499 int position = 0;
5500 for (int i = 0; i < array_length; i++) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005501 int increment = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005502 Object* elt = fixed_array->get(i);
5503 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005504 // Smi encoding of position and length.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005505 int smi_value = Smi::cast(elt)->value();
5506 int pos;
5507 int len;
5508 if (smi_value > 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005509 // Position and length encoded in one smi.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005510 pos = StringBuilderSubstringPosition::decode(smi_value);
5511 len = StringBuilderSubstringLength::decode(smi_value);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005512 } else {
5513 // Position and length encoded in two smis.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005514 len = -smi_value;
5515 // Get the position and check that it is a positive smi.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005516 i++;
5517 if (i >= array_length) {
5518 return Top::Throw(Heap::illegal_argument_symbol());
5519 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005520 Object* next_smi = fixed_array->get(i);
5521 if (!next_smi->IsSmi()) {
5522 return Top::Throw(Heap::illegal_argument_symbol());
5523 }
5524 pos = Smi::cast(next_smi)->value();
5525 if (pos < 0) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005526 return Top::Throw(Heap::illegal_argument_symbol());
5527 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005528 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00005529 ASSERT(pos >= 0);
5530 ASSERT(len >= 0);
5531 if (pos > special_length || len > special_length - pos) {
5532 return Top::Throw(Heap::illegal_argument_symbol());
5533 }
5534 increment = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005535 } else if (elt->IsString()) {
5536 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005537 int element_length = element->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005538 increment = element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005539 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005541 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542 } else {
5543 return Top::Throw(Heap::illegal_argument_symbol());
5544 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005545 if (increment > String::kMaxLength - position) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00005546 Top::context()->mark_out_of_memory();
5547 return Failure::OutOfMemoryException();
5548 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005549 position += increment;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005550 }
5551
5552 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005553 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005554
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005555 if (ascii) {
5556 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005557 if (object->IsFailure()) return object;
5558 SeqAsciiString* answer = SeqAsciiString::cast(object);
5559 StringBuilderConcatHelper(special,
5560 answer->GetChars(),
5561 fixed_array,
5562 array_length);
5563 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005564 } else {
5565 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005566 if (object->IsFailure()) return object;
5567 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
5568 StringBuilderConcatHelper(special,
5569 answer->GetChars(),
5570 fixed_array,
5571 array_length);
5572 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005573 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005574}
5575
5576
5577static Object* Runtime_NumberOr(Arguments args) {
5578 NoHandleAllocation ha;
5579 ASSERT(args.length() == 2);
5580
5581 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5582 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5583 return Heap::NumberFromInt32(x | y);
5584}
5585
5586
5587static Object* Runtime_NumberAnd(Arguments args) {
5588 NoHandleAllocation ha;
5589 ASSERT(args.length() == 2);
5590
5591 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5592 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5593 return Heap::NumberFromInt32(x & y);
5594}
5595
5596
5597static Object* Runtime_NumberXor(Arguments args) {
5598 NoHandleAllocation ha;
5599 ASSERT(args.length() == 2);
5600
5601 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5602 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5603 return Heap::NumberFromInt32(x ^ y);
5604}
5605
5606
5607static Object* Runtime_NumberNot(Arguments args) {
5608 NoHandleAllocation ha;
5609 ASSERT(args.length() == 1);
5610
5611 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5612 return Heap::NumberFromInt32(~x);
5613}
5614
5615
5616static Object* Runtime_NumberShl(Arguments args) {
5617 NoHandleAllocation ha;
5618 ASSERT(args.length() == 2);
5619
5620 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5621 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5622 return Heap::NumberFromInt32(x << (y & 0x1f));
5623}
5624
5625
5626static Object* Runtime_NumberShr(Arguments args) {
5627 NoHandleAllocation ha;
5628 ASSERT(args.length() == 2);
5629
5630 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
5631 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5632 return Heap::NumberFromUint32(x >> (y & 0x1f));
5633}
5634
5635
5636static Object* Runtime_NumberSar(Arguments args) {
5637 NoHandleAllocation ha;
5638 ASSERT(args.length() == 2);
5639
5640 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
5641 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
5642 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
5643}
5644
5645
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005646static Object* Runtime_NumberEquals(Arguments args) {
5647 NoHandleAllocation ha;
5648 ASSERT(args.length() == 2);
5649
5650 CONVERT_DOUBLE_CHECKED(x, args[0]);
5651 CONVERT_DOUBLE_CHECKED(y, args[1]);
5652 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
5653 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
5654 if (x == y) return Smi::FromInt(EQUAL);
5655 Object* result;
5656 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
5657 result = Smi::FromInt(EQUAL);
5658 } else {
5659 result = Smi::FromInt(NOT_EQUAL);
5660 }
5661 return result;
5662}
5663
5664
5665static Object* Runtime_StringEquals(Arguments args) {
5666 NoHandleAllocation ha;
5667 ASSERT(args.length() == 2);
5668
5669 CONVERT_CHECKED(String, x, args[0]);
5670 CONVERT_CHECKED(String, y, args[1]);
5671
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005672 bool not_equal = !x->Equals(y);
5673 // This is slightly convoluted because the value that signifies
5674 // equality is 0 and inequality is 1 so we have to negate the result
5675 // from String::Equals.
5676 ASSERT(not_equal == 0 || not_equal == 1);
5677 STATIC_CHECK(EQUAL == 0);
5678 STATIC_CHECK(NOT_EQUAL == 1);
5679 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005680}
5681
5682
5683static Object* Runtime_NumberCompare(Arguments args) {
5684 NoHandleAllocation ha;
5685 ASSERT(args.length() == 3);
5686
5687 CONVERT_DOUBLE_CHECKED(x, args[0]);
5688 CONVERT_DOUBLE_CHECKED(y, args[1]);
5689 if (isnan(x) || isnan(y)) return args[2];
5690 if (x == y) return Smi::FromInt(EQUAL);
5691 if (isless(x, y)) return Smi::FromInt(LESS);
5692 return Smi::FromInt(GREATER);
5693}
5694
5695
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005696// Compare two Smis as if they were converted to strings and then
5697// compared lexicographically.
5698static Object* Runtime_SmiLexicographicCompare(Arguments args) {
5699 NoHandleAllocation ha;
5700 ASSERT(args.length() == 2);
5701
5702 // Arrays for the individual characters of the two Smis. Smis are
5703 // 31 bit integers and 10 decimal digits are therefore enough.
5704 static int x_elms[10];
5705 static int y_elms[10];
5706
5707 // Extract the integer values from the Smis.
5708 CONVERT_CHECKED(Smi, x, args[0]);
5709 CONVERT_CHECKED(Smi, y, args[1]);
5710 int x_value = x->value();
5711 int y_value = y->value();
5712
5713 // If the integers are equal so are the string representations.
5714 if (x_value == y_value) return Smi::FromInt(EQUAL);
5715
5716 // If one of the integers are zero the normal integer order is the
5717 // same as the lexicographic order of the string representations.
5718 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
5719
ager@chromium.org32912102009-01-16 10:38:43 +00005720 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005721 // smallest because the char code of '-' is less than the char code
5722 // of any digit. Otherwise, we make both values positive.
5723 if (x_value < 0 || y_value < 0) {
5724 if (y_value >= 0) return Smi::FromInt(LESS);
5725 if (x_value >= 0) return Smi::FromInt(GREATER);
5726 x_value = -x_value;
5727 y_value = -y_value;
5728 }
5729
5730 // Convert the integers to arrays of their decimal digits.
5731 int x_index = 0;
5732 int y_index = 0;
5733 while (x_value > 0) {
5734 x_elms[x_index++] = x_value % 10;
5735 x_value /= 10;
5736 }
5737 while (y_value > 0) {
5738 y_elms[y_index++] = y_value % 10;
5739 y_value /= 10;
5740 }
5741
5742 // Loop through the arrays of decimal digits finding the first place
5743 // where they differ.
5744 while (--x_index >= 0 && --y_index >= 0) {
5745 int diff = x_elms[x_index] - y_elms[y_index];
5746 if (diff != 0) return Smi::FromInt(diff);
5747 }
5748
5749 // If one array is a suffix of the other array, the longest array is
5750 // the representation of the largest of the Smis in the
5751 // lexicographic ordering.
5752 return Smi::FromInt(x_index - y_index);
5753}
5754
5755
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005756static Object* StringInputBufferCompare(String* x, String* y) {
5757 static StringInputBuffer bufx;
5758 static StringInputBuffer bufy;
5759 bufx.Reset(x);
5760 bufy.Reset(y);
5761 while (bufx.has_more() && bufy.has_more()) {
5762 int d = bufx.GetNext() - bufy.GetNext();
5763 if (d < 0) return Smi::FromInt(LESS);
5764 else if (d > 0) return Smi::FromInt(GREATER);
5765 }
5766
5767 // x is (non-trivial) prefix of y:
5768 if (bufy.has_more()) return Smi::FromInt(LESS);
5769 // y is prefix of x:
5770 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
5771}
5772
5773
5774static Object* FlatStringCompare(String* x, String* y) {
5775 ASSERT(x->IsFlat());
5776 ASSERT(y->IsFlat());
5777 Object* equal_prefix_result = Smi::FromInt(EQUAL);
5778 int prefix_length = x->length();
5779 if (y->length() < prefix_length) {
5780 prefix_length = y->length();
5781 equal_prefix_result = Smi::FromInt(GREATER);
5782 } else if (y->length() > prefix_length) {
5783 equal_prefix_result = Smi::FromInt(LESS);
5784 }
5785 int r;
5786 if (x->IsAsciiRepresentation()) {
5787 Vector<const char> x_chars = x->ToAsciiVector();
5788 if (y->IsAsciiRepresentation()) {
5789 Vector<const char> y_chars = y->ToAsciiVector();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005790 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005791 } else {
5792 Vector<const uc16> y_chars = y->ToUC16Vector();
5793 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5794 }
5795 } else {
5796 Vector<const uc16> x_chars = x->ToUC16Vector();
5797 if (y->IsAsciiRepresentation()) {
5798 Vector<const char> y_chars = y->ToAsciiVector();
5799 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5800 } else {
5801 Vector<const uc16> y_chars = y->ToUC16Vector();
5802 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
5803 }
5804 }
5805 Object* result;
5806 if (r == 0) {
5807 result = equal_prefix_result;
5808 } else {
5809 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
5810 }
5811 ASSERT(result == StringInputBufferCompare(x, y));
5812 return result;
5813}
5814
5815
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005816static Object* Runtime_StringCompare(Arguments args) {
5817 NoHandleAllocation ha;
5818 ASSERT(args.length() == 2);
5819
5820 CONVERT_CHECKED(String, x, args[0]);
5821 CONVERT_CHECKED(String, y, args[1]);
5822
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005823 Counters::string_compare_runtime.Increment();
5824
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005825 // A few fast case tests before we flatten.
5826 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005827 if (y->length() == 0) {
5828 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005829 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005830 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005831 return Smi::FromInt(LESS);
5832 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005833
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005834 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005835 if (d < 0) return Smi::FromInt(LESS);
5836 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005837
fschneider@chromium.org086aac62010-03-17 13:18:24 +00005838 Object* obj = Heap::PrepareForCompare(x);
5839 if (obj->IsFailure()) return obj;
5840 obj = Heap::PrepareForCompare(y);
5841 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005843 return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
5844 : StringInputBufferCompare(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005845}
5846
5847
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005848static Object* Runtime_Math_acos(Arguments args) {
5849 NoHandleAllocation ha;
5850 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005851 Counters::math_acos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005852
5853 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005854 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005855}
5856
5857
5858static Object* Runtime_Math_asin(Arguments args) {
5859 NoHandleAllocation ha;
5860 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005861 Counters::math_asin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005862
5863 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005864 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005865}
5866
5867
5868static Object* Runtime_Math_atan(Arguments args) {
5869 NoHandleAllocation ha;
5870 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005871 Counters::math_atan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005872
5873 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005874 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005875}
5876
5877
5878static Object* Runtime_Math_atan2(Arguments args) {
5879 NoHandleAllocation ha;
5880 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005881 Counters::math_atan2.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005882
5883 CONVERT_DOUBLE_CHECKED(x, args[0]);
5884 CONVERT_DOUBLE_CHECKED(y, args[1]);
5885 double result;
5886 if (isinf(x) && isinf(y)) {
5887 // Make sure that the result in case of two infinite arguments
5888 // is a multiple of Pi / 4. The sign of the result is determined
5889 // by the first argument (x) and the sign of the second argument
5890 // determines the multiplier: one or three.
5891 static double kPiDividedBy4 = 0.78539816339744830962;
5892 int multiplier = (x < 0) ? -1 : 1;
5893 if (y < 0) multiplier *= 3;
5894 result = multiplier * kPiDividedBy4;
5895 } else {
5896 result = atan2(x, y);
5897 }
5898 return Heap::AllocateHeapNumber(result);
5899}
5900
5901
5902static Object* Runtime_Math_ceil(Arguments args) {
5903 NoHandleAllocation ha;
5904 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005905 Counters::math_ceil.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005906
5907 CONVERT_DOUBLE_CHECKED(x, args[0]);
5908 return Heap::NumberFromDouble(ceiling(x));
5909}
5910
5911
5912static Object* Runtime_Math_cos(Arguments args) {
5913 NoHandleAllocation ha;
5914 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005915 Counters::math_cos.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005916
5917 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005918 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005919}
5920
5921
5922static Object* Runtime_Math_exp(Arguments args) {
5923 NoHandleAllocation ha;
5924 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005925 Counters::math_exp.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005926
5927 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005928 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005929}
5930
5931
5932static Object* Runtime_Math_floor(Arguments args) {
5933 NoHandleAllocation ha;
5934 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005935 Counters::math_floor.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005936
5937 CONVERT_DOUBLE_CHECKED(x, args[0]);
5938 return Heap::NumberFromDouble(floor(x));
5939}
5940
5941
5942static Object* Runtime_Math_log(Arguments args) {
5943 NoHandleAllocation ha;
5944 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005945 Counters::math_log.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005946
5947 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00005948 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005949}
5950
5951
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005952// Helper function to compute x^y, where y is known to be an
5953// integer. Uses binary decomposition to limit the number of
5954// multiplications; see the discussion in "Hacker's Delight" by Henry
5955// S. Warren, Jr., figure 11-6, page 213.
5956static double powi(double x, int y) {
5957 ASSERT(y != kMinInt);
5958 unsigned n = (y < 0) ? -y : y;
5959 double m = x;
5960 double p = 1;
5961 while (true) {
5962 if ((n & 1) != 0) p *= m;
5963 n >>= 1;
5964 if (n == 0) {
5965 if (y < 0) {
5966 // Unfortunately, we have to be careful when p has reached
5967 // infinity in the computation, because sometimes the higher
5968 // internal precision in the pow() implementation would have
5969 // given us a finite p. This happens very rarely.
5970 double result = 1.0 / p;
5971 return (result == 0 && isinf(p))
5972 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
5973 : result;
5974 } else {
5975 return p;
5976 }
5977 }
5978 m *= m;
5979 }
5980}
5981
5982
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005983static Object* Runtime_Math_pow(Arguments args) {
5984 NoHandleAllocation ha;
5985 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00005986 Counters::math_pow.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005987
5988 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00005989
5990 // If the second argument is a smi, it is much faster to call the
5991 // custom powi() function than the generic pow().
5992 if (args[1]->IsSmi()) {
5993 int y = Smi::cast(args[1])->value();
5994 return Heap::AllocateHeapNumber(powi(x, y));
5995 }
5996
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005997 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00005998
5999 if (!isinf(x)) {
6000 if (y == 0.5) {
6001 // It's not uncommon to use Math.pow(x, 0.5) to compute the
6002 // square root of a number. To speed up such computations, we
6003 // explictly check for this case and use the sqrt() function
6004 // which is faster than pow().
6005 return Heap::AllocateHeapNumber(sqrt(x));
6006 } else if (y == -0.5) {
6007 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
6008 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
6009 }
6010 }
6011
6012 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006013 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006014 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6015 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006016 } else {
6017 return Heap::AllocateHeapNumber(pow(x, y));
6018 }
6019}
6020
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006021// Fast version of Math.pow if we know that y is not an integer and
6022// y is not -0.5 or 0.5. Used as slowcase from codegen.
6023static Object* Runtime_Math_pow_cfunction(Arguments args) {
6024 NoHandleAllocation ha;
6025 ASSERT(args.length() == 2);
6026 CONVERT_DOUBLE_CHECKED(x, args[0]);
6027 CONVERT_DOUBLE_CHECKED(y, args[1]);
6028 if (y == 0) {
6029 return Smi::FromInt(1);
6030 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
6031 return Heap::nan_value();
6032 } else {
6033 return Heap::AllocateHeapNumber(pow(x, y));
6034 }
6035}
6036
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006037
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006038static Object* Runtime_RoundNumber(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006039 NoHandleAllocation ha;
6040 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006041 Counters::math_round.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006042
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006043 if (!args[0]->IsHeapNumber()) {
6044 // Must be smi. Return the argument unchanged for all the other types
6045 // to make fuzz-natives test happy.
6046 return args[0];
6047 }
6048
6049 HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
6050
6051 double value = number->value();
6052 int exponent = number->get_exponent();
6053 int sign = number->get_sign();
6054
6055 // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
6056 // should be rounded to 2^30, which is not smi.
6057 if (!sign && exponent <= kSmiValueSize - 3) {
6058 return Smi::FromInt(static_cast<int>(value + 0.5));
6059 }
6060
6061 // If the magnitude is big enough, there's no place for fraction part. If we
6062 // try to add 0.5 to this number, 1.0 will be added instead.
6063 if (exponent >= 52) {
6064 return number;
6065 }
6066
6067 if (sign && value >= -0.5) return Heap::minus_zero_value();
6068
6069 return Heap::NumberFromDouble(floor(value + 0.5));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006070}
6071
6072
6073static Object* Runtime_Math_sin(Arguments args) {
6074 NoHandleAllocation ha;
6075 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006076 Counters::math_sin.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006077
6078 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006079 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006080}
6081
6082
6083static Object* Runtime_Math_sqrt(Arguments args) {
6084 NoHandleAllocation ha;
6085 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006086 Counters::math_sqrt.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006087
6088 CONVERT_DOUBLE_CHECKED(x, args[0]);
6089 return Heap::AllocateHeapNumber(sqrt(x));
6090}
6091
6092
6093static Object* Runtime_Math_tan(Arguments args) {
6094 NoHandleAllocation ha;
6095 ASSERT(args.length() == 1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006096 Counters::math_tan.Increment();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006097
6098 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006099 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006100}
6101
6102
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006103static int MakeDay(int year, int month, int day) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006104 static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
6105 181, 212, 243, 273, 304, 334};
6106 static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
6107 182, 213, 244, 274, 305, 335};
6108
6109 year += month / 12;
6110 month %= 12;
6111 if (month < 0) {
6112 year--;
6113 month += 12;
6114 }
6115
6116 ASSERT(month >= 0);
6117 ASSERT(month < 12);
6118
6119 // year_delta is an arbitrary number such that:
6120 // a) year_delta = -1 (mod 400)
6121 // b) year + year_delta > 0 for years in the range defined by
6122 // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
6123 // Jan 1 1970. This is required so that we don't run into integer
6124 // division of negative numbers.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006125 // c) there shouldn't be an overflow for 32-bit integers in the following
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006126 // operations.
6127 static const int year_delta = 399999;
6128 static const int base_day = 365 * (1970 + year_delta) +
6129 (1970 + year_delta) / 4 -
6130 (1970 + year_delta) / 100 +
6131 (1970 + year_delta) / 400;
6132
6133 int year1 = year + year_delta;
6134 int day_from_year = 365 * year1 +
6135 year1 / 4 -
6136 year1 / 100 +
6137 year1 / 400 -
6138 base_day;
6139
6140 if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006141 return day_from_year + day_from_month[month] + day - 1;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006142 }
6143
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006144 return day_from_year + day_from_month_leap[month] + day - 1;
6145}
6146
6147
6148static Object* Runtime_DateMakeDay(Arguments args) {
6149 NoHandleAllocation ha;
6150 ASSERT(args.length() == 3);
6151
6152 CONVERT_SMI_CHECKED(year, args[0]);
6153 CONVERT_SMI_CHECKED(month, args[1]);
6154 CONVERT_SMI_CHECKED(date, args[2]);
6155
6156 return Smi::FromInt(MakeDay(year, month, date));
6157}
6158
6159
6160static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
6161static const int kDaysIn4Years = 4 * 365 + 1;
6162static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
6163static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
6164static const int kDays1970to2000 = 30 * 365 + 7;
6165static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
6166 kDays1970to2000;
6167static const int kYearsOffset = 400000;
6168
6169static const char kDayInYear[] = {
6170 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6171 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6172 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6173 22, 23, 24, 25, 26, 27, 28,
6174 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6175 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6176 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6177 22, 23, 24, 25, 26, 27, 28, 29, 30,
6178 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6179 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6180 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6181 22, 23, 24, 25, 26, 27, 28, 29, 30,
6182 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6183 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6184 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6185 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6186 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6187 22, 23, 24, 25, 26, 27, 28, 29, 30,
6188 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6189 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6190 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6191 22, 23, 24, 25, 26, 27, 28, 29, 30,
6192 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6193 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6194
6195 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6196 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6197 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6198 22, 23, 24, 25, 26, 27, 28,
6199 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6200 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6201 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6202 22, 23, 24, 25, 26, 27, 28, 29, 30,
6203 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6204 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6205 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6206 22, 23, 24, 25, 26, 27, 28, 29, 30,
6207 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6208 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6209 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6210 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6211 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6212 22, 23, 24, 25, 26, 27, 28, 29, 30,
6213 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6214 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6215 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6216 22, 23, 24, 25, 26, 27, 28, 29, 30,
6217 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6218 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6219
6220 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6221 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6222 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6223 22, 23, 24, 25, 26, 27, 28, 29,
6224 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6225 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6226 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6227 22, 23, 24, 25, 26, 27, 28, 29, 30,
6228 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6229 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6230 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6231 22, 23, 24, 25, 26, 27, 28, 29, 30,
6232 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6233 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6234 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6235 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6236 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6237 22, 23, 24, 25, 26, 27, 28, 29, 30,
6238 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6239 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6240 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6241 22, 23, 24, 25, 26, 27, 28, 29, 30,
6242 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6243 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6244
6245 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6246 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6247 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6248 22, 23, 24, 25, 26, 27, 28,
6249 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6250 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6251 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6252 22, 23, 24, 25, 26, 27, 28, 29, 30,
6253 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6254 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6255 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6256 22, 23, 24, 25, 26, 27, 28, 29, 30,
6257 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6258 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6259 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6260 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6261 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6262 22, 23, 24, 25, 26, 27, 28, 29, 30,
6263 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6264 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
6265 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6266 22, 23, 24, 25, 26, 27, 28, 29, 30,
6267 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
6268 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
6269
6270static const char kMonthInYear[] = {
6271 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,
6272 0, 0, 0, 0, 0, 0,
6273 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,
6274 1, 1, 1,
6275 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,
6276 2, 2, 2, 2, 2, 2,
6277 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,
6278 3, 3, 3, 3, 3,
6279 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,
6280 4, 4, 4, 4, 4, 4,
6281 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,
6282 5, 5, 5, 5, 5,
6283 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,
6284 6, 6, 6, 6, 6, 6,
6285 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,
6286 7, 7, 7, 7, 7, 7,
6287 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,
6288 8, 8, 8, 8, 8,
6289 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,
6290 9, 9, 9, 9, 9, 9,
6291 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6292 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6293 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6294 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6295
6296 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,
6297 0, 0, 0, 0, 0, 0,
6298 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,
6299 1, 1, 1,
6300 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,
6301 2, 2, 2, 2, 2, 2,
6302 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,
6303 3, 3, 3, 3, 3,
6304 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,
6305 4, 4, 4, 4, 4, 4,
6306 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,
6307 5, 5, 5, 5, 5,
6308 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,
6309 6, 6, 6, 6, 6, 6,
6310 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,
6311 7, 7, 7, 7, 7, 7,
6312 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,
6313 8, 8, 8, 8, 8,
6314 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,
6315 9, 9, 9, 9, 9, 9,
6316 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6317 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6318 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6319 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6320
6321 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,
6322 0, 0, 0, 0, 0, 0,
6323 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,
6324 1, 1, 1, 1,
6325 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,
6326 2, 2, 2, 2, 2, 2,
6327 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,
6328 3, 3, 3, 3, 3,
6329 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,
6330 4, 4, 4, 4, 4, 4,
6331 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,
6332 5, 5, 5, 5, 5,
6333 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,
6334 6, 6, 6, 6, 6, 6,
6335 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,
6336 7, 7, 7, 7, 7, 7,
6337 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,
6338 8, 8, 8, 8, 8,
6339 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,
6340 9, 9, 9, 9, 9, 9,
6341 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6342 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6343 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6344 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6345
6346 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,
6347 0, 0, 0, 0, 0, 0,
6348 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,
6349 1, 1, 1,
6350 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,
6351 2, 2, 2, 2, 2, 2,
6352 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,
6353 3, 3, 3, 3, 3,
6354 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,
6355 4, 4, 4, 4, 4, 4,
6356 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,
6357 5, 5, 5, 5, 5,
6358 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,
6359 6, 6, 6, 6, 6, 6,
6360 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,
6361 7, 7, 7, 7, 7, 7,
6362 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,
6363 8, 8, 8, 8, 8,
6364 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,
6365 9, 9, 9, 9, 9, 9,
6366 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6367 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
6368 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
6369 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
6370
6371
6372// This function works for dates from 1970 to 2099.
6373static inline void DateYMDFromTimeAfter1970(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006374 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006375#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006376 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006377#endif
6378
6379 year = 1970 + (4 * date + 2) / kDaysIn4Years;
6380 date %= kDaysIn4Years;
6381
6382 month = kMonthInYear[date];
6383 day = kDayInYear[date];
6384
6385 ASSERT(MakeDay(year, month, day) == save_date);
6386}
6387
6388
6389static inline void DateYMDFromTimeSlow(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006390 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006391#ifdef DEBUG
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00006392 int save_date = date; // Need this for ASSERT in the end.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006393#endif
6394
6395 date += kDaysOffset;
6396 year = 400 * (date / kDaysIn400Years) - kYearsOffset;
6397 date %= kDaysIn400Years;
6398
6399 ASSERT(MakeDay(year, 0, 1) + date == save_date);
6400
6401 date--;
6402 int yd1 = date / kDaysIn100Years;
6403 date %= kDaysIn100Years;
6404 year += 100 * yd1;
6405
6406 date++;
6407 int yd2 = date / kDaysIn4Years;
6408 date %= kDaysIn4Years;
6409 year += 4 * yd2;
6410
6411 date--;
6412 int yd3 = date / 365;
6413 date %= 365;
6414 year += yd3;
6415
6416 bool is_leap = (!yd1 || yd2) && !yd3;
6417
6418 ASSERT(date >= -1);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006419 ASSERT(is_leap || (date >= 0));
6420 ASSERT((date < 365) || (is_leap && (date < 366)));
6421 ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
6422 ASSERT(is_leap || ((MakeDay(year, 0, 1) + date) == save_date));
6423 ASSERT(!is_leap || ((MakeDay(year, 0, 1) + date + 1) == save_date));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006424
6425 if (is_leap) {
6426 day = kDayInYear[2*365 + 1 + date];
6427 month = kMonthInYear[2*365 + 1 + date];
6428 } else {
6429 day = kDayInYear[date];
6430 month = kMonthInYear[date];
6431 }
6432
6433 ASSERT(MakeDay(year, month, day) == save_date);
6434}
6435
6436
6437static inline void DateYMDFromTime(int date,
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00006438 int& year, int& month, int& day) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00006439 if (date >= 0 && date < 32 * kDaysIn4Years) {
6440 DateYMDFromTimeAfter1970(date, year, month, day);
6441 } else {
6442 DateYMDFromTimeSlow(date, year, month, day);
6443 }
6444}
6445
6446
6447static Object* Runtime_DateYMDFromTime(Arguments args) {
6448 NoHandleAllocation ha;
6449 ASSERT(args.length() == 2);
6450
6451 CONVERT_DOUBLE_CHECKED(t, args[0]);
6452 CONVERT_CHECKED(JSArray, res_array, args[1]);
6453
6454 int year, month, day;
6455 DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
6456
6457 res_array->SetElement(0, Smi::FromInt(year));
6458 res_array->SetElement(1, Smi::FromInt(month));
6459 res_array->SetElement(2, Smi::FromInt(day));
6460
6461 return Heap::undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006462}
6463
6464
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006465static Object* Runtime_NewArgumentsFast(Arguments args) {
6466 NoHandleAllocation ha;
6467 ASSERT(args.length() == 3);
6468
6469 JSFunction* callee = JSFunction::cast(args[0]);
6470 Object** parameters = reinterpret_cast<Object**>(args[1]);
6471 const int length = Smi::cast(args[2])->value();
6472
6473 Object* result = Heap::AllocateArgumentsObject(callee, length);
6474 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006475 // Allocate the elements if needed.
6476 if (length > 0) {
6477 // Allocate the fixed array.
6478 Object* obj = Heap::AllocateRawFixedArray(length);
6479 if (obj->IsFailure()) return obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006480
6481 AssertNoAllocation no_gc;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006482 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
6483 FixedArray* array = FixedArray::cast(obj);
6484 array->set_length(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006485
6486 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006487 for (int i = 0; i < length; i++) {
6488 array->set(i, *--parameters, mode);
6489 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006490 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006491 }
6492 return result;
6493}
6494
6495
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006496static Object* Runtime_NewClosure(Arguments args) {
6497 HandleScope scope;
6498 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00006499 CONVERT_ARG_CHECKED(Context, context, 0);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006500 CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006501
sgjesse@chromium.org846fb742009-12-18 08:56:33 +00006502 PretenureFlag pretenure = (context->global_context() == *context)
6503 ? TENURED // Allocate global closures in old space.
6504 : NOT_TENURED; // Allocate local closures in new space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006505 Handle<JSFunction> result =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006506 Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006507 return *result;
6508}
6509
6510
ager@chromium.org5c838252010-02-19 08:53:10 +00006511static Code* ComputeConstructStub(Handle<JSFunction> function) {
6512 Handle<Object> prototype = Factory::null_value();
6513 if (function->has_instance_prototype()) {
6514 prototype = Handle<Object>(function->instance_prototype());
6515 }
6516 if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006517 ConstructStubCompiler compiler;
ager@chromium.org5c838252010-02-19 08:53:10 +00006518 Object* code = compiler.CompileConstructStub(function->shared());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006519 if (code->IsFailure()) {
6520 return Builtins::builtin(Builtins::JSConstructStubGeneric);
6521 }
6522 return Code::cast(code);
6523 }
6524
ager@chromium.org5c838252010-02-19 08:53:10 +00006525 return function->shared()->construct_stub();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006526}
6527
6528
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006529static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006530 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006531 ASSERT(args.length() == 1);
6532
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006533 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006534
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006535 // If the constructor isn't a proper function we throw a type error.
6536 if (!constructor->IsJSFunction()) {
6537 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
6538 Handle<Object> type_error =
6539 Factory::NewTypeError("not_constructor", arguments);
6540 return Top::Throw(*type_error);
6541 }
6542
6543 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006544#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006545 // Handle stepping into constructors if step into is active.
6546 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00006547 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006548 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006549#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006550
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006551 if (function->has_initial_map()) {
6552 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006553 // The 'Function' function ignores the receiver object when
6554 // called using 'new' and creates a new JSFunction object that
6555 // is returned. The receiver object is only used for error
6556 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006557 // JSFunction. Factory::NewJSObject() should not be used to
6558 // allocate JSFunctions since it does not properly initialize
6559 // the shared part of the function. Since the receiver is
6560 // ignored anyway, we use the global object as the receiver
6561 // instead of a new JSFunction object. This way, errors are
6562 // reported the same way whether or not 'Function' is called
6563 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006564 return Top::context()->global();
6565 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006566 }
6567
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006568 // The function should be compiled for the optimization hints to be available.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006569 Handle<SharedFunctionInfo> shared(function->shared());
6570 EnsureCompiled(shared, CLEAR_EXCEPTION);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006571
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006572 bool first_allocation = !function->has_initial_map();
6573 Handle<JSObject> result = Factory::NewJSObject(function);
6574 if (first_allocation) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006575 Handle<Code> stub = Handle<Code>(
ager@chromium.org5c838252010-02-19 08:53:10 +00006576 ComputeConstructStub(Handle<JSFunction>(function)));
6577 shared->set_construct_stub(*stub);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006578 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006579
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00006580 Counters::constructed_objects.Increment();
6581 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00006582
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006583 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006584}
6585
6586
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006587static Object* Runtime_LazyCompile(Arguments args) {
6588 HandleScope scope;
6589 ASSERT(args.length() == 1);
6590
6591 Handle<JSFunction> function = args.at<JSFunction>(0);
6592#ifdef DEBUG
6593 if (FLAG_trace_lazy) {
6594 PrintF("[lazy: ");
6595 function->shared()->name()->Print();
6596 PrintF("]\n");
6597 }
6598#endif
6599
kasperl@chromium.org71affb52009-05-26 05:44:31 +00006600 // Compile the target function. Here we compile using CompileLazyInLoop in
6601 // order to get the optimized version. This helps code like delta-blue
6602 // that calls performance-critical routines through constructors. A
6603 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
6604 // direct call. Since the in-loop tracking takes place through CallICs
6605 // this means that things called through constructors are never known to
6606 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006607 ASSERT(!function->is_compiled());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006608 if (!CompileLazyInLoop(function, Handle<Object>::null(), KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006609 return Failure::Exception();
6610 }
6611
6612 return function->code();
6613}
6614
6615
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006616static Object* Runtime_GetFunctionDelegate(Arguments args) {
6617 HandleScope scope;
6618 ASSERT(args.length() == 1);
6619 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6620 return *Execution::GetFunctionDelegate(args.at<Object>(0));
6621}
6622
6623
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00006624static Object* Runtime_GetConstructorDelegate(Arguments args) {
6625 HandleScope scope;
6626 ASSERT(args.length() == 1);
6627 RUNTIME_ASSERT(!args[0]->IsJSFunction());
6628 return *Execution::GetConstructorDelegate(args.at<Object>(0));
6629}
6630
6631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006632static Object* Runtime_NewContext(Arguments args) {
6633 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00006634 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006635
kasper.lund7276f142008-07-30 08:49:36 +00006636 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006637 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
6638 Object* result = Heap::AllocateFunctionContext(length, function);
6639 if (result->IsFailure()) return result;
6640
6641 Top::set_context(Context::cast(result));
6642
kasper.lund7276f142008-07-30 08:49:36 +00006643 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006644}
6645
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006646static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006647 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006648 Object* js_object = object;
6649 if (!js_object->IsJSObject()) {
6650 js_object = js_object->ToObject();
6651 if (js_object->IsFailure()) {
6652 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006653 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006654 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006655 Handle<Object> result =
6656 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
6657 return Top::Throw(*result);
6658 }
6659 }
6660
6661 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006662 Heap::AllocateWithContext(Top::context(),
6663 JSObject::cast(js_object),
6664 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006665 if (result->IsFailure()) return result;
6666
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006667 Context* context = Context::cast(result);
6668 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006669
kasper.lund7276f142008-07-30 08:49:36 +00006670 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006671}
6672
6673
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006674static Object* Runtime_PushContext(Arguments args) {
6675 NoHandleAllocation ha;
6676 ASSERT(args.length() == 1);
6677 return PushContextHelper(args[0], false);
6678}
6679
6680
6681static Object* Runtime_PushCatchContext(Arguments args) {
6682 NoHandleAllocation ha;
6683 ASSERT(args.length() == 1);
6684 return PushContextHelper(args[0], true);
6685}
6686
6687
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006688static Object* Runtime_LookupContext(Arguments args) {
6689 HandleScope scope;
6690 ASSERT(args.length() == 2);
6691
6692 CONVERT_ARG_CHECKED(Context, context, 0);
6693 CONVERT_ARG_CHECKED(String, name, 1);
6694
6695 int index;
6696 PropertyAttributes attributes;
6697 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006698 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006699 context->Lookup(name, flags, &index, &attributes);
6700
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006701 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006702 ASSERT(holder->IsJSObject());
6703 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006704 }
6705
6706 // No intermediate context found. Use global object by default.
6707 return Top::context()->global();
6708}
6709
6710
ager@chromium.orga1645e22009-09-09 19:27:10 +00006711// A mechanism to return a pair of Object pointers in registers (if possible).
6712// How this is achieved is calling convention-dependent.
6713// All currently supported x86 compiles uses calling conventions that are cdecl
6714// variants where a 64-bit value is returned in two 32-bit registers
6715// (edx:eax on ia32, r1:r0 on ARM).
6716// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
6717// In Win64 calling convention, a struct of two pointers is returned in memory,
6718// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006719#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006720struct ObjectPair {
6721 Object* x;
6722 Object* y;
6723};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006724
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006725static inline ObjectPair MakePair(Object* x, Object* y) {
6726 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00006727 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
6728 // In Win64 they are assigned to a hidden first argument.
6729 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006730}
6731#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006732typedef uint64_t ObjectPair;
6733static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006734 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006735 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006736}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006737#endif
6738
6739
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006740static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006741 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
6742 USE(attributes);
6743 return x->IsTheHole() ? Heap::undefined_value() : x;
6744}
6745
6746
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006747static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
6748 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006749 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006750 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006751 JSFunction* context_extension_function =
6752 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006753 // If the holder isn't a context extension object, we just return it
6754 // as the receiver. This allows arguments objects to be used as
6755 // receivers, but only if they are put in the context scope chain
6756 // explicitly via a with-statement.
6757 Object* constructor = holder->map()->constructor();
6758 if (constructor != context_extension_function) return holder;
6759 // Fall back to using the global object as the receiver if the
6760 // property turns out to be a local variable allocated in a context
6761 // extension object - introduced via eval.
6762 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006763}
6764
6765
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006766static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006767 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00006768 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006769
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006770 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00006771 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006772 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006773 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006774 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006775
6776 int index;
6777 PropertyAttributes attributes;
6778 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006779 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006780 context->Lookup(name, flags, &index, &attributes);
6781
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006782 // If the index is non-negative, the slot has been found in a local
6783 // variable or a parameter. Read it from the context object or the
6784 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006785 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006786 // If the "property" we were looking for is a local variable or an
6787 // argument in a context, the receiver is the global object; see
6788 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
6789 JSObject* receiver = Top::context()->global()->global_receiver();
6790 Object* value = (holder->IsContext())
6791 ? Context::cast(*holder)->get(index)
6792 : JSObject::cast(*holder)->GetElement(index);
6793 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006794 }
6795
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006796 // If the holder is found, we read the property from it.
6797 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006798 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006799 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006800 JSObject* receiver;
6801 if (object->IsGlobalObject()) {
6802 receiver = GlobalObject::cast(object)->global_receiver();
6803 } else if (context->is_exception_holder(*holder)) {
6804 receiver = Top::context()->global()->global_receiver();
6805 } else {
6806 receiver = ComputeReceiverForNonGlobal(object);
6807 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006808 // No need to unhole the value here. This is taken care of by the
6809 // GetProperty function.
6810 Object* value = object->GetProperty(*name);
6811 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006812 }
6813
6814 if (throw_error) {
6815 // The property doesn't exist - throw exception.
6816 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006817 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006818 return MakePair(Top::Throw(*reference_error), NULL);
6819 } else {
6820 // The property doesn't exist - return undefined
6821 return MakePair(Heap::undefined_value(), Heap::undefined_value());
6822 }
6823}
6824
6825
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006826static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006827 return LoadContextSlotHelper(args, true);
6828}
6829
6830
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006831static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006832 return LoadContextSlotHelper(args, false);
6833}
6834
6835
6836static Object* Runtime_StoreContextSlot(Arguments args) {
6837 HandleScope scope;
6838 ASSERT(args.length() == 3);
6839
6840 Handle<Object> value(args[0]);
6841 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006842 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006843
6844 int index;
6845 PropertyAttributes attributes;
6846 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006847 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006848 context->Lookup(name, flags, &index, &attributes);
6849
6850 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006851 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006852 // Ignore if read_only variable.
6853 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006854 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006855 }
6856 } else {
6857 ASSERT((attributes & READ_ONLY) == 0);
6858 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006859 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006860 USE(result);
6861 ASSERT(!result->IsFailure());
6862 }
6863 return *value;
6864 }
6865
6866 // Slow case: The property is not in a FixedArray context.
6867 // It is either in an JSObject extension context or it was not found.
6868 Handle<JSObject> context_ext;
6869
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006870 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006871 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006872 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006873 } else {
6874 // The property was not found. It needs to be stored in the global context.
6875 ASSERT(attributes == ABSENT);
6876 attributes = NONE;
6877 context_ext = Handle<JSObject>(Top::context()->global());
6878 }
6879
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006880 // Set the property, but ignore if read_only variable on the context
6881 // extension object itself.
6882 if ((attributes & READ_ONLY) == 0 ||
6883 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006884 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
6885 if (set.is_null()) {
6886 // Failure::Exception is converted to a null handle in the
6887 // handle-based methods such as SetProperty. We therefore need
6888 // to convert null handles back to exceptions.
6889 ASSERT(Top::has_pending_exception());
6890 return Failure::Exception();
6891 }
6892 }
6893 return *value;
6894}
6895
6896
6897static Object* Runtime_Throw(Arguments args) {
6898 HandleScope scope;
6899 ASSERT(args.length() == 1);
6900
6901 return Top::Throw(args[0]);
6902}
6903
6904
6905static Object* Runtime_ReThrow(Arguments args) {
6906 HandleScope scope;
6907 ASSERT(args.length() == 1);
6908
6909 return Top::ReThrow(args[0]);
6910}
6911
6912
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006913static Object* Runtime_PromoteScheduledException(Arguments args) {
6914 ASSERT_EQ(0, args.length());
6915 return Top::PromoteScheduledException();
6916}
6917
6918
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006919static Object* Runtime_ThrowReferenceError(Arguments args) {
6920 HandleScope scope;
6921 ASSERT(args.length() == 1);
6922
6923 Handle<Object> name(args[0]);
6924 Handle<Object> reference_error =
6925 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
6926 return Top::Throw(*reference_error);
6927}
6928
6929
6930static Object* Runtime_StackOverflow(Arguments args) {
6931 NoHandleAllocation na;
6932 return Top::StackOverflow();
6933}
6934
6935
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006936static Object* Runtime_StackGuard(Arguments args) {
6937 ASSERT(args.length() == 1);
6938
6939 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006940 if (StackGuard::IsStackOverflow()) {
6941 return Runtime_StackOverflow(args);
6942 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006943
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006944 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006945}
6946
6947
6948// NOTE: These PrintXXX functions are defined for all builds (not just
6949// DEBUG builds) because we may want to be able to trace function
6950// calls in all modes.
6951static void PrintString(String* str) {
6952 // not uncommon to have empty strings
6953 if (str->length() > 0) {
6954 SmartPointer<char> s =
6955 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
6956 PrintF("%s", *s);
6957 }
6958}
6959
6960
6961static void PrintObject(Object* obj) {
6962 if (obj->IsSmi()) {
6963 PrintF("%d", Smi::cast(obj)->value());
6964 } else if (obj->IsString() || obj->IsSymbol()) {
6965 PrintString(String::cast(obj));
6966 } else if (obj->IsNumber()) {
6967 PrintF("%g", obj->Number());
6968 } else if (obj->IsFailure()) {
6969 PrintF("<failure>");
6970 } else if (obj->IsUndefined()) {
6971 PrintF("<undefined>");
6972 } else if (obj->IsNull()) {
6973 PrintF("<null>");
6974 } else if (obj->IsTrue()) {
6975 PrintF("<true>");
6976 } else if (obj->IsFalse()) {
6977 PrintF("<false>");
6978 } else {
6979 PrintF("%p", obj);
6980 }
6981}
6982
6983
6984static int StackSize() {
6985 int n = 0;
6986 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
6987 return n;
6988}
6989
6990
6991static void PrintTransition(Object* result) {
6992 // indentation
6993 { const int nmax = 80;
6994 int n = StackSize();
6995 if (n <= nmax)
6996 PrintF("%4d:%*s", n, n, "");
6997 else
6998 PrintF("%4d:%*s", n, nmax, "...");
6999 }
7000
7001 if (result == NULL) {
7002 // constructor calls
7003 JavaScriptFrameIterator it;
7004 JavaScriptFrame* frame = it.frame();
7005 if (frame->IsConstructor()) PrintF("new ");
7006 // function name
7007 Object* fun = frame->function();
7008 if (fun->IsJSFunction()) {
7009 PrintObject(JSFunction::cast(fun)->shared()->name());
7010 } else {
7011 PrintObject(fun);
7012 }
7013 // function arguments
7014 // (we are intentionally only printing the actually
7015 // supplied parameters, not all parameters required)
7016 PrintF("(this=");
7017 PrintObject(frame->receiver());
7018 const int length = frame->GetProvidedParametersCount();
7019 for (int i = 0; i < length; i++) {
7020 PrintF(", ");
7021 PrintObject(frame->GetParameter(i));
7022 }
7023 PrintF(") {\n");
7024
7025 } else {
7026 // function result
7027 PrintF("} -> ");
7028 PrintObject(result);
7029 PrintF("\n");
7030 }
7031}
7032
7033
7034static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007035 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007036 NoHandleAllocation ha;
7037 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007038 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007039}
7040
7041
7042static Object* Runtime_TraceExit(Arguments args) {
7043 NoHandleAllocation ha;
7044 PrintTransition(args[0]);
7045 return args[0]; // return TOS
7046}
7047
7048
7049static Object* Runtime_DebugPrint(Arguments args) {
7050 NoHandleAllocation ha;
7051 ASSERT(args.length() == 1);
7052
7053#ifdef DEBUG
7054 if (args[0]->IsString()) {
7055 // If we have a string, assume it's a code "marker"
7056 // and print some interesting cpu debugging info.
7057 JavaScriptFrameIterator it;
7058 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007059 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
7060 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007061 } else {
7062 PrintF("DebugPrint: ");
7063 }
7064 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007065 if (args[0]->IsHeapObject()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007066 PrintF("\n");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007067 HeapObject::cast(args[0])->map()->Print();
7068 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007069#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007070 // ShortPrint is available in release mode. Print is not.
7071 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007072#endif
7073 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00007074 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007075
7076 return args[0]; // return TOS
7077}
7078
7079
7080static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007081 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007082 NoHandleAllocation ha;
7083 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007084 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007085}
7086
7087
mads.s.ager31e71382008-08-13 09:32:07 +00007088static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007089 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007090 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007091
7092 // According to ECMA-262, section 15.9.1, page 117, the precision of
7093 // the number in a Date object representing a particular instant in
7094 // time is milliseconds. Therefore, we floor the result of getting
7095 // the OS time.
7096 double millis = floor(OS::TimeCurrentMillis());
7097 return Heap::NumberFromDouble(millis);
7098}
7099
7100
7101static Object* Runtime_DateParseString(Arguments args) {
7102 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007103 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007104
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007105 CONVERT_ARG_CHECKED(String, str, 0);
7106 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007107
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007108 CONVERT_ARG_CHECKED(JSArray, output, 1);
7109 RUNTIME_ASSERT(output->HasFastElements());
7110
7111 AssertNoAllocation no_allocation;
7112
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007113 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007114 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
7115 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007116 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007117 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007118 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007119 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00007120 result = DateParser::Parse(str->ToUC16Vector(), output_array);
7121 }
7122
7123 if (result) {
7124 return *output;
7125 } else {
7126 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007127 }
7128}
7129
7130
7131static Object* Runtime_DateLocalTimezone(Arguments args) {
7132 NoHandleAllocation ha;
7133 ASSERT(args.length() == 1);
7134
7135 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00007136 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007137 return Heap::AllocateStringFromUtf8(CStrVector(zone));
7138}
7139
7140
7141static Object* Runtime_DateLocalTimeOffset(Arguments args) {
7142 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00007143 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007144
7145 return Heap::NumberFromDouble(OS::LocalTimeOffset());
7146}
7147
7148
7149static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
7150 NoHandleAllocation ha;
7151 ASSERT(args.length() == 1);
7152
7153 CONVERT_DOUBLE_CHECKED(x, args[0]);
7154 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
7155}
7156
7157
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007158static Object* Runtime_GlobalReceiver(Arguments args) {
7159 ASSERT(args.length() == 1);
7160 Object* global = args[0];
7161 if (!global->IsJSGlobalObject()) return Heap::null_value();
7162 return JSGlobalObject::cast(global)->global_receiver();
7163}
7164
7165
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007166static Object* Runtime_CompileString(Arguments args) {
7167 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007168 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00007169 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007170 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007171
ager@chromium.org381abbb2009-02-25 13:23:22 +00007172 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007173 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007174 Compiler::ValidationState validate = (is_json->IsTrue())
7175 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007176 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
7177 context,
7178 true,
7179 validate);
7180 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007181 Handle<JSFunction> fun =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007182 Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007183 return *fun;
7184}
7185
7186
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007187static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
7188 ASSERT(args.length() == 3);
7189 if (!args[0]->IsJSFunction()) {
7190 return MakePair(Top::ThrowIllegalOperation(), NULL);
7191 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007192
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007193 HandleScope scope;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007194 Handle<JSFunction> callee = args.at<JSFunction>(0);
7195 Handle<Object> receiver; // Will be overwritten.
7196
7197 // Compute the calling context.
7198 Handle<Context> context = Handle<Context>(Top::context());
7199#ifdef DEBUG
7200 // Make sure Top::context() agrees with the old code that traversed
7201 // the stack frames to compute the context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007202 StackFrameLocator locator;
7203 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007204 ASSERT(Context::cast(frame->context()) == *context);
7205#endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007206
7207 // Find where the 'eval' symbol is bound. It is unaliased only if
7208 // it is bound in the global context.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007209 int index = -1;
7210 PropertyAttributes attributes = ABSENT;
7211 while (true) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007212 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
7213 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007214 // Stop search when eval is found or when the global context is
7215 // reached.
7216 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007217 if (context->is_function_context()) {
7218 context = Handle<Context>(Context::cast(context->closure()->context()));
7219 } else {
7220 context = Handle<Context>(context->previous());
7221 }
7222 }
7223
iposva@chromium.org245aa852009-02-10 00:49:54 +00007224 // If eval could not be resolved, it has been deleted and we need to
7225 // throw a reference error.
7226 if (attributes == ABSENT) {
7227 Handle<Object> name = Factory::eval_symbol();
7228 Handle<Object> reference_error =
7229 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007230 return MakePair(Top::Throw(*reference_error), NULL);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007231 }
7232
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007233 if (!context->IsGlobalContext()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007234 // 'eval' is not bound in the global context. Just call the function
7235 // with the given arguments. This is not necessarily the global eval.
7236 if (receiver->IsContext()) {
7237 context = Handle<Context>::cast(receiver);
7238 receiver = Handle<Object>(context->get(index));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007239 } else if (receiver->IsJSContextExtensionObject()) {
7240 receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007241 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007242 return MakePair(*callee, *receiver);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007243 }
7244
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007245 // 'eval' is bound in the global context, but it may have been overwritten.
7246 // Compare it to the builtin 'GlobalEval' function to make sure.
7247 if (*callee != Top::global_context()->global_eval_fun() ||
7248 !args[1]->IsString()) {
7249 return MakePair(*callee, Top::context()->global()->global_receiver());
7250 }
7251
7252 // Deal with a normal eval call with a string argument. Compile it
7253 // and return the compiled function bound in the local context.
7254 Handle<String> source = args.at<String>(1);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007255 Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007256 source,
7257 Handle<Context>(Top::context()),
7258 Top::context()->IsGlobalContext(),
7259 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007260 if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
7261 callee = Factory::NewFunctionFromSharedFunctionInfo(
7262 shared,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007263 Handle<Context>(Top::context()),
7264 NOT_TENURED);
7265 return MakePair(*callee, args[2]);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007266}
7267
7268
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007269static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
7270 // This utility adjusts the property attributes for newly created Function
7271 // object ("new Function(...)") by changing the map.
7272 // All it does is changing the prototype property to enumerable
7273 // as specified in ECMA262, 15.3.5.2.
7274 HandleScope scope;
7275 ASSERT(args.length() == 1);
7276 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7277 ASSERT(func->map()->instance_type() ==
7278 Top::function_instance_map()->instance_type());
7279 ASSERT(func->map()->instance_size() ==
7280 Top::function_instance_map()->instance_size());
7281 func->set_map(*Top::function_instance_map());
7282 return *func;
7283}
7284
7285
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007286// Push an array unto an array of arrays if it is not already in the
7287// array. Returns true if the element was pushed on the stack and
7288// false otherwise.
7289static Object* Runtime_PushIfAbsent(Arguments args) {
7290 ASSERT(args.length() == 2);
7291 CONVERT_CHECKED(JSArray, array, args[0]);
7292 CONVERT_CHECKED(JSArray, element, args[1]);
7293 RUNTIME_ASSERT(array->HasFastElements());
7294 int length = Smi::cast(array->length())->value();
7295 FixedArray* elements = FixedArray::cast(array->elements());
7296 for (int i = 0; i < length; i++) {
7297 if (elements->get(i) == element) return Heap::false_value();
7298 }
7299 Object* obj = array->SetFastElement(length, element);
7300 if (obj->IsFailure()) return obj;
7301 return Heap::true_value();
7302}
7303
7304
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007305/**
7306 * A simple visitor visits every element of Array's.
7307 * The backend storage can be a fixed array for fast elements case,
7308 * or a dictionary for sparse array. Since Dictionary is a subtype
7309 * of FixedArray, the class can be used by both fast and slow cases.
7310 * The second parameter of the constructor, fast_elements, specifies
7311 * whether the storage is a FixedArray or Dictionary.
7312 *
7313 * An index limit is used to deal with the situation that a result array
7314 * length overflows 32-bit non-negative integer.
7315 */
7316class ArrayConcatVisitor {
7317 public:
7318 ArrayConcatVisitor(Handle<FixedArray> storage,
7319 uint32_t index_limit,
7320 bool fast_elements) :
7321 storage_(storage), index_limit_(index_limit),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007322 index_offset_(0), fast_elements_(fast_elements) { }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007323
7324 void visit(uint32_t i, Handle<Object> elm) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007325 if (i >= index_limit_ - index_offset_) return;
7326 uint32_t index = index_offset_ + i;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007327
7328 if (fast_elements_) {
7329 ASSERT(index < static_cast<uint32_t>(storage_->length()));
7330 storage_->set(index, *elm);
7331
7332 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007333 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
7334 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007335 Factory::DictionaryAtNumberPut(dict, index, elm);
7336 if (!result.is_identical_to(dict))
7337 storage_ = result;
7338 }
7339 }
7340
7341 void increase_index_offset(uint32_t delta) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007342 if (index_limit_ - index_offset_ < delta) {
7343 index_offset_ = index_limit_;
7344 } else {
7345 index_offset_ += delta;
7346 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007347 }
7348
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007349 Handle<FixedArray> storage() { return storage_; }
7350
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007351 private:
7352 Handle<FixedArray> storage_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007353 // Limit on the accepted indices. Elements with indices larger than the
7354 // limit are ignored by the visitor.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007355 uint32_t index_limit_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007356 // Index after last seen index. Always less than or equal to index_limit_.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007357 uint32_t index_offset_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007358 bool fast_elements_;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007359};
7360
7361
ager@chromium.org3811b432009-10-28 14:53:37 +00007362template<class ExternalArrayClass, class ElementType>
7363static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
7364 bool elements_are_ints,
7365 bool elements_are_guaranteed_smis,
7366 uint32_t range,
7367 ArrayConcatVisitor* visitor) {
7368 Handle<ExternalArrayClass> array(
7369 ExternalArrayClass::cast(receiver->elements()));
7370 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
7371
7372 if (visitor != NULL) {
7373 if (elements_are_ints) {
7374 if (elements_are_guaranteed_smis) {
7375 for (uint32_t j = 0; j < len; j++) {
7376 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
7377 visitor->visit(j, e);
7378 }
7379 } else {
7380 for (uint32_t j = 0; j < len; j++) {
7381 int64_t val = static_cast<int64_t>(array->get(j));
7382 if (Smi::IsValid(static_cast<intptr_t>(val))) {
7383 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
7384 visitor->visit(j, e);
7385 } else {
7386 Handle<Object> e(
7387 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
7388 visitor->visit(j, e);
7389 }
7390 }
7391 }
7392 } else {
7393 for (uint32_t j = 0; j < len; j++) {
7394 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
7395 visitor->visit(j, e);
7396 }
7397 }
7398 }
7399
7400 return len;
7401}
7402
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007403/**
7404 * A helper function that visits elements of a JSObject. Only elements
7405 * whose index between 0 and range (exclusive) are visited.
7406 *
7407 * If the third parameter, visitor, is not NULL, the visitor is called
7408 * with parameters, 'visitor_index_offset + element index' and the element.
7409 *
7410 * It returns the number of visisted elements.
7411 */
7412static uint32_t IterateElements(Handle<JSObject> receiver,
7413 uint32_t range,
7414 ArrayConcatVisitor* visitor) {
7415 uint32_t num_of_elements = 0;
7416
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007417 switch (receiver->GetElementsKind()) {
7418 case JSObject::FAST_ELEMENTS: {
7419 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
7420 uint32_t len = elements->length();
7421 if (range < len) {
7422 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007423 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007424
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007425 for (uint32_t j = 0; j < len; j++) {
7426 Handle<Object> e(elements->get(j));
7427 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007428 num_of_elements++;
7429 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007430 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007431 }
7432 }
7433 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007434 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007435 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007436 case JSObject::PIXEL_ELEMENTS: {
7437 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
7438 uint32_t len = pixels->length();
7439 if (range < len) {
7440 len = range;
7441 }
7442
7443 for (uint32_t j = 0; j < len; j++) {
7444 num_of_elements++;
7445 if (visitor != NULL) {
7446 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
7447 visitor->visit(j, e);
7448 }
7449 }
7450 break;
7451 }
ager@chromium.org3811b432009-10-28 14:53:37 +00007452 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
7453 num_of_elements =
7454 IterateExternalArrayElements<ExternalByteArray, int8_t>(
7455 receiver, true, true, range, visitor);
7456 break;
7457 }
7458 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
7459 num_of_elements =
7460 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
7461 receiver, true, true, range, visitor);
7462 break;
7463 }
7464 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
7465 num_of_elements =
7466 IterateExternalArrayElements<ExternalShortArray, int16_t>(
7467 receiver, true, true, range, visitor);
7468 break;
7469 }
7470 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
7471 num_of_elements =
7472 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
7473 receiver, true, true, range, visitor);
7474 break;
7475 }
7476 case JSObject::EXTERNAL_INT_ELEMENTS: {
7477 num_of_elements =
7478 IterateExternalArrayElements<ExternalIntArray, int32_t>(
7479 receiver, true, false, range, visitor);
7480 break;
7481 }
7482 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
7483 num_of_elements =
7484 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
7485 receiver, true, false, range, visitor);
7486 break;
7487 }
7488 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
7489 num_of_elements =
7490 IterateExternalArrayElements<ExternalFloatArray, float>(
7491 receiver, false, false, range, visitor);
7492 break;
7493 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007494 case JSObject::DICTIONARY_ELEMENTS: {
7495 Handle<NumberDictionary> dict(receiver->element_dictionary());
7496 uint32_t capacity = dict->Capacity();
7497 for (uint32_t j = 0; j < capacity; j++) {
7498 Handle<Object> k(dict->KeyAt(j));
7499 if (dict->IsKey(*k)) {
7500 ASSERT(k->IsNumber());
7501 uint32_t index = static_cast<uint32_t>(k->Number());
7502 if (index < range) {
7503 num_of_elements++;
7504 if (visitor) {
7505 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
7506 }
7507 }
7508 }
7509 }
7510 break;
7511 }
7512 default:
7513 UNREACHABLE();
7514 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007515 }
7516
7517 return num_of_elements;
7518}
7519
7520
7521/**
7522 * A helper function that visits elements of an Array object, and elements
7523 * on its prototypes.
7524 *
7525 * Elements on prototypes are visited first, and only elements whose indices
7526 * less than Array length are visited.
7527 *
7528 * If a ArrayConcatVisitor object is given, the visitor is called with
7529 * parameters, element's index + visitor_index_offset and the element.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007530 *
7531 * The returned number of elements is an upper bound on the actual number
7532 * of elements added. If the same element occurs in more than one object
7533 * in the array's prototype chain, it will be counted more than once, but
7534 * will only occur once in the result.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007535 */
7536static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
7537 ArrayConcatVisitor* visitor) {
7538 uint32_t range = static_cast<uint32_t>(array->length()->Number());
7539 Handle<Object> obj = array;
7540
7541 static const int kEstimatedPrototypes = 3;
7542 List< Handle<JSObject> > objects(kEstimatedPrototypes);
7543
7544 // Visit prototype first. If an element on the prototype is shadowed by
7545 // the inheritor using the same index, the ArrayConcatVisitor visits
7546 // the prototype element before the shadowing element.
7547 // The visitor can simply overwrite the old value by new value using
7548 // the same index. This follows Array::concat semantics.
7549 while (!obj->IsNull()) {
7550 objects.Add(Handle<JSObject>::cast(obj));
7551 obj = Handle<Object>(obj->GetPrototype());
7552 }
7553
7554 uint32_t nof_elements = 0;
7555 for (int i = objects.length() - 1; i >= 0; i--) {
7556 Handle<JSObject> obj = objects[i];
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007557 uint32_t encountered_elements =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007558 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007559
7560 if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
7561 nof_elements = JSObject::kMaxElementCount;
7562 } else {
7563 nof_elements += encountered_elements;
7564 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007565 }
7566
7567 return nof_elements;
7568}
7569
7570
7571/**
7572 * A helper function of Runtime_ArrayConcat.
7573 *
7574 * The first argument is an Array of arrays and objects. It is the
7575 * same as the arguments array of Array::concat JS function.
7576 *
7577 * If an argument is an Array object, the function visits array
7578 * elements. If an argument is not an Array object, the function
7579 * visits the object as if it is an one-element array.
7580 *
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007581 * If the result array index overflows 32-bit unsigned integer, the rounded
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007582 * non-negative number is used as new length. For example, if one
7583 * array length is 2^32 - 1, second array length is 1, the
7584 * concatenated array length is 0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007585 * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
7586 * is one more than the last array index to get a value assigned).
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007587 */
7588static uint32_t IterateArguments(Handle<JSArray> arguments,
7589 ArrayConcatVisitor* visitor) {
7590 uint32_t visited_elements = 0;
7591 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7592
7593 for (uint32_t i = 0; i < num_of_args; i++) {
7594 Handle<Object> obj(arguments->GetElement(i));
7595 if (obj->IsJSArray()) {
7596 Handle<JSArray> array = Handle<JSArray>::cast(obj);
7597 uint32_t len = static_cast<uint32_t>(array->length()->Number());
7598 uint32_t nof_elements =
7599 IterateArrayAndPrototypeElements(array, visitor);
7600 // Total elements of array and its prototype chain can be more than
7601 // the array length, but ArrayConcat can only concatenate at most
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007602 // the array length number of elements. We use the length as an estimate
7603 // for the actual number of elements added.
7604 uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
7605 if (JSArray::kMaxElementCount - visited_elements < added_elements) {
7606 visited_elements = JSArray::kMaxElementCount;
7607 } else {
7608 visited_elements += added_elements;
7609 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007610 if (visitor) visitor->increase_index_offset(len);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007611 } else {
7612 if (visitor) {
7613 visitor->visit(0, obj);
7614 visitor->increase_index_offset(1);
7615 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007616 if (visited_elements < JSArray::kMaxElementCount) {
7617 visited_elements++;
7618 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007619 }
7620 }
7621 return visited_elements;
7622}
7623
7624
7625/**
7626 * Array::concat implementation.
7627 * See ECMAScript 262, 15.4.4.4.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007628 * TODO(lrn): Fix non-compliance for very large concatenations and update to
7629 * following the ECMAScript 5 specification.
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007630 */
7631static Object* Runtime_ArrayConcat(Arguments args) {
7632 ASSERT(args.length() == 1);
7633 HandleScope handle_scope;
7634
7635 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
7636 Handle<JSArray> arguments(arg_arrays);
7637
7638 // Pass 1: estimate the number of elements of the result
7639 // (it could be more than real numbers if prototype has elements).
7640 uint32_t result_length = 0;
7641 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
7642
7643 { AssertNoAllocation nogc;
7644 for (uint32_t i = 0; i < num_of_args; i++) {
7645 Object* obj = arguments->GetElement(i);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007646 uint32_t length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007647 if (obj->IsJSArray()) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007648 length_estimate =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007649 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
7650 } else {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007651 length_estimate = 1;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007652 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007653 if (JSObject::kMaxElementCount - result_length < length_estimate) {
7654 result_length = JSObject::kMaxElementCount;
7655 break;
7656 }
7657 result_length += length_estimate;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007658 }
7659 }
7660
7661 // Allocate an empty array, will set length and content later.
7662 Handle<JSArray> result = Factory::NewJSArray(0);
7663
7664 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
7665 // If estimated number of elements is more than half of length, a
7666 // fixed array (fast case) is more time and space-efficient than a
7667 // dictionary.
7668 bool fast_case = (estimate_nof_elements * 2) >= result_length;
7669
7670 Handle<FixedArray> storage;
7671 if (fast_case) {
7672 // The backing storage array must have non-existing elements to
7673 // preserve holes across concat operations.
7674 storage = Factory::NewFixedArrayWithHoles(result_length);
7675
7676 } else {
7677 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
7678 uint32_t at_least_space_for = estimate_nof_elements +
7679 (estimate_nof_elements >> 2);
7680 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007681 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007682 }
7683
7684 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
7685
7686 ArrayConcatVisitor visitor(storage, result_length, fast_case);
7687
7688 IterateArguments(arguments, &visitor);
7689
7690 result->set_length(*len);
kasperl@chromium.orgedf0cd12010-01-05 13:29:12 +00007691 // Please note the storage might have changed in the visitor.
7692 result->set_elements(*visitor.storage());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007693
7694 return *result;
7695}
7696
7697
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007698// This will not allocate (flatten the string), but it may run
7699// very slowly for very deeply nested ConsStrings. For debugging use only.
7700static Object* Runtime_GlobalPrint(Arguments args) {
7701 NoHandleAllocation ha;
7702 ASSERT(args.length() == 1);
7703
7704 CONVERT_CHECKED(String, string, args[0]);
7705 StringInputBuffer buffer(string);
7706 while (buffer.has_more()) {
7707 uint16_t character = buffer.GetNext();
7708 PrintF("%c", character);
7709 }
7710 return string;
7711}
7712
ager@chromium.org5ec48922009-05-05 07:25:34 +00007713// Moves all own elements of an object, that are below a limit, to positions
7714// starting at zero. All undefined values are placed after non-undefined values,
7715// and are followed by non-existing element. Does not change the length
7716// property.
7717// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007718static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00007719 ASSERT(args.length() == 2);
7720 CONVERT_CHECKED(JSObject, object, args[0]);
7721 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
7722 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007723}
7724
7725
7726// Move contents of argument 0 (an array) to argument 1 (an array)
7727static Object* Runtime_MoveArrayContents(Arguments args) {
7728 ASSERT(args.length() == 2);
7729 CONVERT_CHECKED(JSArray, from, args[0]);
7730 CONVERT_CHECKED(JSArray, to, args[1]);
7731 to->SetContent(FixedArray::cast(from->elements()));
7732 to->set_length(from->length());
7733 from->SetContent(Heap::empty_fixed_array());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007734 from->set_length(Smi::FromInt(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007735 return to;
7736}
7737
7738
7739// How many elements does this array have?
7740static Object* Runtime_EstimateNumberOfElements(Arguments args) {
7741 ASSERT(args.length() == 1);
7742 CONVERT_CHECKED(JSArray, array, args[0]);
7743 HeapObject* elements = array->elements();
7744 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007745 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007746 } else {
7747 return array->length();
7748 }
7749}
7750
7751
7752// Returns an array that tells you where in the [0, length) interval an array
7753// might have elements. Can either return keys or intervals. Keys can have
7754// gaps in (undefined). Intervals can also span over some undefined keys.
7755static Object* Runtime_GetArrayKeys(Arguments args) {
7756 ASSERT(args.length() == 2);
7757 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00007758 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007759 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007760 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007761 // Create an array and get all the keys into it, then remove all the
7762 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007763 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007764 int keys_length = keys->length();
7765 for (int i = 0; i < keys_length; i++) {
7766 Object* key = keys->get(i);
7767 uint32_t index;
7768 if (!Array::IndexFromObject(key, &index) || index >= length) {
7769 // Zap invalid keys.
7770 keys->set_undefined(i);
7771 }
7772 }
7773 return *Factory::NewJSArrayWithElements(keys);
7774 } else {
7775 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
7776 // -1 means start of array.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007777 single_interval->set(0, Smi::FromInt(-1));
ager@chromium.org5ec48922009-05-05 07:25:34 +00007778 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
7779 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007780 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00007781 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007782 single_interval->set(1, *length_object);
7783 return *Factory::NewJSArrayWithElements(single_interval);
7784 }
7785}
7786
7787
7788// DefineAccessor takes an optional final argument which is the
7789// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
7790// to the way accessors are implemented, it is set for both the getter
7791// and setter on the first call to DefineAccessor and ignored on
7792// subsequent calls.
7793static Object* Runtime_DefineAccessor(Arguments args) {
7794 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
7795 // Compute attributes.
7796 PropertyAttributes attributes = NONE;
7797 if (args.length() == 5) {
7798 CONVERT_CHECKED(Smi, attrs, args[4]);
7799 int value = attrs->value();
7800 // Only attribute bits should be set.
7801 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
7802 attributes = static_cast<PropertyAttributes>(value);
7803 }
7804
7805 CONVERT_CHECKED(JSObject, obj, args[0]);
7806 CONVERT_CHECKED(String, name, args[1]);
7807 CONVERT_CHECKED(Smi, flag, args[2]);
7808 CONVERT_CHECKED(JSFunction, fun, args[3]);
7809 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
7810}
7811
7812
7813static Object* Runtime_LookupAccessor(Arguments args) {
7814 ASSERT(args.length() == 3);
7815 CONVERT_CHECKED(JSObject, obj, args[0]);
7816 CONVERT_CHECKED(String, name, args[1]);
7817 CONVERT_CHECKED(Smi, flag, args[2]);
7818 return obj->LookupAccessor(name, flag->value() == 0);
7819}
7820
7821
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007822#ifdef ENABLE_DEBUGGER_SUPPORT
7823static Object* Runtime_DebugBreak(Arguments args) {
7824 ASSERT(args.length() == 0);
7825 return Execution::DebugBreakHelper();
7826}
7827
7828
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007829// Helper functions for wrapping and unwrapping stack frame ids.
7830static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007831 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007832 return Smi::FromInt(id >> 2);
7833}
7834
7835
7836static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
7837 return static_cast<StackFrame::Id>(wrapped->value() << 2);
7838}
7839
7840
7841// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00007842// args[0]: debug event listener function to set or null or undefined for
7843// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007844// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00007845static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007846 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007847 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
7848 args[0]->IsUndefined() ||
7849 args[0]->IsNull());
7850 Handle<Object> callback = args.at<Object>(0);
7851 Handle<Object> data = args.at<Object>(1);
7852 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007853
7854 return Heap::undefined_value();
7855}
7856
7857
7858static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007859 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007860 StackGuard::DebugBreak();
7861 return Heap::undefined_value();
7862}
7863
7864
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007865static Object* DebugLookupResultValue(Object* receiver, String* name,
7866 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00007867 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007868 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007869 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007870 case NORMAL:
7871 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007872 if (value->IsTheHole()) {
7873 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007874 }
7875 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007876 case FIELD:
7877 value =
7878 JSObject::cast(
7879 result->holder())->FastPropertyAt(result->GetFieldIndex());
7880 if (value->IsTheHole()) {
7881 return Heap::undefined_value();
7882 }
7883 return value;
7884 case CONSTANT_FUNCTION:
7885 return result->GetConstantFunction();
7886 case CALLBACKS: {
7887 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007888 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007889 value = receiver->GetPropertyWithCallback(
7890 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00007891 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00007892 value = Top::pending_exception();
7893 Top::clear_pending_exception();
7894 if (caught_exception != NULL) {
7895 *caught_exception = true;
7896 }
7897 }
7898 return value;
7899 } else {
7900 return Heap::undefined_value();
7901 }
7902 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007903 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007904 case MAP_TRANSITION:
7905 case CONSTANT_TRANSITION:
7906 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007907 return Heap::undefined_value();
7908 default:
7909 UNREACHABLE();
7910 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007911 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007912 return Heap::undefined_value();
7913}
7914
7915
ager@chromium.org32912102009-01-16 10:38:43 +00007916// Get debugger related details for an object property.
7917// args[0]: object holding property
7918// args[1]: name of the property
7919//
7920// The array returned contains the following information:
7921// 0: Property value
7922// 1: Property details
7923// 2: Property value is exception
7924// 3: Getter function if defined
7925// 4: Setter function if defined
7926// Items 2-4 are only filled if the property has either a getter or a setter
7927// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007928static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007929 HandleScope scope;
7930
7931 ASSERT(args.length() == 2);
7932
7933 CONVERT_ARG_CHECKED(JSObject, obj, 0);
7934 CONVERT_ARG_CHECKED(String, name, 1);
7935
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00007936 // Make sure to set the current context to the context before the debugger was
7937 // entered (if the debugger is entered). The reason for switching context here
7938 // is that for some property lookups (accessors and interceptors) callbacks
7939 // into the embedding application can occour, and the embedding application
7940 // could have the assumption that its own global context is the current
7941 // context and not some internal debugger context.
7942 SaveContext save;
7943 if (Debug::InDebugger()) {
7944 Top::set_context(*Debug::debugger_entry()->GetContext());
7945 }
7946
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007947 // Skip the global proxy as it has no properties and always delegates to the
7948 // real global object.
7949 if (obj->IsJSGlobalProxy()) {
7950 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
7951 }
7952
7953
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007954 // Check if the name is trivially convertible to an index and get the element
7955 // if so.
7956 uint32_t index;
7957 if (name->AsArrayIndex(&index)) {
7958 Handle<FixedArray> details = Factory::NewFixedArray(2);
7959 details->set(0, Runtime::GetElementOrCharAt(obj, index));
7960 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
7961 return *Factory::NewJSArrayWithElements(details);
7962 }
7963
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007964 // Find the number of objects making up this.
7965 int length = LocalPrototypeChainLength(*obj);
7966
7967 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007968 Handle<JSObject> jsproto = obj;
7969 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007970 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007971 jsproto->LocalLookup(*name, &result);
7972 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00007973 // LookupResult is not GC safe as it holds raw object pointers.
7974 // GC can happen later in this code so put the required fields into
7975 // local variables using handles when required for later use.
7976 PropertyType result_type = result.type();
7977 Handle<Object> result_callback_obj;
7978 if (result_type == CALLBACKS) {
7979 result_callback_obj = Handle<Object>(result.GetCallbackObject());
7980 }
7981 Smi* property_details = result.GetPropertyDetails().AsSmi();
7982 // DebugLookupResultValue can cause GC so details from LookupResult needs
7983 // to be copied to handles before this.
7984 bool caught_exception = false;
7985 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
7986 &caught_exception);
7987 if (raw_value->IsFailure()) return raw_value;
7988 Handle<Object> value(raw_value);
7989
7990 // If the callback object is a fixed array then it contains JavaScript
7991 // getter and/or setter.
7992 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
7993 result_callback_obj->IsFixedArray();
7994 Handle<FixedArray> details =
7995 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
7996 details->set(0, *value);
7997 details->set(1, property_details);
7998 if (hasJavaScriptAccessors) {
7999 details->set(2,
8000 caught_exception ? Heap::true_value()
8001 : Heap::false_value());
8002 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
8003 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
8004 }
8005
8006 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00008007 }
8008 if (i < length - 1) {
8009 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
8010 }
8011 }
8012
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008013 return Heap::undefined_value();
8014}
8015
8016
8017static Object* Runtime_DebugGetProperty(Arguments args) {
8018 HandleScope scope;
8019
8020 ASSERT(args.length() == 2);
8021
8022 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8023 CONVERT_ARG_CHECKED(String, name, 1);
8024
8025 LookupResult result;
8026 obj->Lookup(*name, &result);
8027 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00008028 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008029 }
8030 return Heap::undefined_value();
8031}
8032
8033
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008034// Return the property type calculated from the property details.
8035// args[0]: smi with property details.
8036static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
8037 ASSERT(args.length() == 1);
8038 CONVERT_CHECKED(Smi, details, args[0]);
8039 PropertyType type = PropertyDetails(details).type();
8040 return Smi::FromInt(static_cast<int>(type));
8041}
8042
8043
8044// Return the property attribute calculated from the property details.
8045// args[0]: smi with property details.
8046static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
8047 ASSERT(args.length() == 1);
8048 CONVERT_CHECKED(Smi, details, args[0]);
8049 PropertyAttributes attributes = PropertyDetails(details).attributes();
8050 return Smi::FromInt(static_cast<int>(attributes));
8051}
8052
8053
8054// Return the property insertion index calculated from the property details.
8055// args[0]: smi with property details.
8056static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
8057 ASSERT(args.length() == 1);
8058 CONVERT_CHECKED(Smi, details, args[0]);
8059 int index = PropertyDetails(details).index();
8060 return Smi::FromInt(index);
8061}
8062
8063
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008064// Return property value from named interceptor.
8065// args[0]: object
8066// args[1]: property name
8067static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
8068 HandleScope scope;
8069 ASSERT(args.length() == 2);
8070 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8071 RUNTIME_ASSERT(obj->HasNamedInterceptor());
8072 CONVERT_ARG_CHECKED(String, name, 1);
8073
8074 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008075 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008076}
8077
8078
8079// Return element value from indexed interceptor.
8080// args[0]: object
8081// args[1]: index
8082static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
8083 HandleScope scope;
8084 ASSERT(args.length() == 2);
8085 CONVERT_ARG_CHECKED(JSObject, obj, 0);
8086 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
8087 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
8088
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008089 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008090}
8091
8092
8093static Object* Runtime_CheckExecutionState(Arguments args) {
8094 ASSERT(args.length() >= 1);
8095 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00008096 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008097 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008098 return Top::Throw(Heap::illegal_execution_state_symbol());
8099 }
8100
8101 return Heap::true_value();
8102}
8103
8104
8105static Object* Runtime_GetFrameCount(Arguments args) {
8106 HandleScope scope;
8107 ASSERT(args.length() == 1);
8108
8109 // Check arguments.
8110 Object* result = Runtime_CheckExecutionState(args);
8111 if (result->IsFailure()) return result;
8112
8113 // Count all frames which are relevant to debugging stack trace.
8114 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008115 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008116 if (id == StackFrame::NO_ID) {
8117 // If there is no JavaScript stack frame count is 0.
8118 return Smi::FromInt(0);
8119 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008120 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
8121 return Smi::FromInt(n);
8122}
8123
8124
8125static const int kFrameDetailsFrameIdIndex = 0;
8126static const int kFrameDetailsReceiverIndex = 1;
8127static const int kFrameDetailsFunctionIndex = 2;
8128static const int kFrameDetailsArgumentCountIndex = 3;
8129static const int kFrameDetailsLocalCountIndex = 4;
8130static const int kFrameDetailsSourcePositionIndex = 5;
8131static const int kFrameDetailsConstructCallIndex = 6;
8132static const int kFrameDetailsDebuggerFrameIndex = 7;
8133static const int kFrameDetailsFirstDynamicIndex = 8;
8134
8135// Return an array with frame details
8136// args[0]: number: break id
8137// args[1]: number: frame index
8138//
8139// The array returned contains the following information:
8140// 0: Frame id
8141// 1: Receiver
8142// 2: Function
8143// 3: Argument count
8144// 4: Local count
8145// 5: Source position
8146// 6: Constructor call
8147// 7: Debugger frame
8148// Arguments name, value
8149// Locals name, value
8150static Object* Runtime_GetFrameDetails(Arguments args) {
8151 HandleScope scope;
8152 ASSERT(args.length() == 2);
8153
8154 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00008155 Object* check = Runtime_CheckExecutionState(args);
8156 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008157 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8158
8159 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00008160 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00008161 if (id == StackFrame::NO_ID) {
8162 // If there are no JavaScript stack frames return undefined.
8163 return Heap::undefined_value();
8164 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008165 int count = 0;
8166 JavaScriptFrameIterator it(id);
8167 for (; !it.done(); it.Advance()) {
8168 if (count == index) break;
8169 count++;
8170 }
8171 if (it.done()) return Heap::undefined_value();
8172
8173 // Traverse the saved contexts chain to find the active context for the
8174 // selected frame.
8175 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008176 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008177 save = save->prev();
8178 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00008179 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008180
8181 // Get the frame id.
8182 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
8183
8184 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008185 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008186
8187 // Check for constructor frame.
8188 bool constructor = it.frame()->IsConstructor();
8189
8190 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00008191 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008192 ScopeInfo<> info(*code);
8193
8194 // Get the context.
8195 Handle<Context> context(Context::cast(it.frame()->context()));
8196
8197 // Get the locals names and values into a temporary array.
8198 //
8199 // TODO(1240907): Hide compiler-introduced stack variables
8200 // (e.g. .result)? For users of the debugger, they will probably be
8201 // confusing.
8202 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
8203 for (int i = 0; i < info.NumberOfLocals(); i++) {
8204 // Name of the local.
8205 locals->set(i * 2, *info.LocalName(i));
8206
8207 // Fetch the value of the local - either from the stack or from a
8208 // heap-allocated context.
8209 if (i < info.number_of_stack_slots()) {
8210 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
8211 } else {
8212 Handle<String> name = info.LocalName(i);
8213 // Traverse the context chain to the function context as all local
8214 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00008215 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008216 context = Handle<Context>(context->previous());
8217 }
8218 ASSERT(context->is_function_context());
8219 locals->set(i * 2 + 1,
8220 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
8221 NULL)));
8222 }
8223 }
8224
8225 // Now advance to the arguments adapter frame (if any). If contains all
8226 // the provided parameters and
8227
8228 // Now advance to the arguments adapter frame (if any). It contains all
8229 // the provided parameters whereas the function frame always have the number
8230 // of arguments matching the functions parameters. The rest of the
8231 // information (except for what is collected above) is the same.
8232 it.AdvanceToArgumentsFrame();
8233
8234 // Find the number of arguments to fill. At least fill the number of
8235 // parameters for the function and fill more if more parameters are provided.
8236 int argument_count = info.number_of_parameters();
8237 if (argument_count < it.frame()->GetProvidedParametersCount()) {
8238 argument_count = it.frame()->GetProvidedParametersCount();
8239 }
8240
8241 // Calculate the size of the result.
8242 int details_size = kFrameDetailsFirstDynamicIndex +
8243 2 * (argument_count + info.NumberOfLocals());
8244 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8245
8246 // Add the frame id.
8247 details->set(kFrameDetailsFrameIdIndex, *frame_id);
8248
8249 // Add the function (same as in function frame).
8250 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
8251
8252 // Add the arguments count.
8253 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
8254
8255 // Add the locals count
8256 details->set(kFrameDetailsLocalCountIndex,
8257 Smi::FromInt(info.NumberOfLocals()));
8258
8259 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00008260 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008261 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
8262 } else {
8263 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
8264 }
8265
8266 // Add the constructor information.
8267 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
8268
8269 // Add information on whether this frame is invoked in the debugger context.
8270 details->set(kFrameDetailsDebuggerFrameIndex,
8271 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
8272
8273 // Fill the dynamic part.
8274 int details_index = kFrameDetailsFirstDynamicIndex;
8275
8276 // Add arguments name and value.
8277 for (int i = 0; i < argument_count; i++) {
8278 // Name of the argument.
8279 if (i < info.number_of_parameters()) {
8280 details->set(details_index++, *info.parameter_name(i));
8281 } else {
8282 details->set(details_index++, Heap::undefined_value());
8283 }
8284
8285 // Parameter value.
8286 if (i < it.frame()->GetProvidedParametersCount()) {
8287 details->set(details_index++, it.frame()->GetParameter(i));
8288 } else {
8289 details->set(details_index++, Heap::undefined_value());
8290 }
8291 }
8292
8293 // Add locals name and value from the temporary copy from the function frame.
8294 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
8295 details->set(details_index++, locals->get(i));
8296 }
8297
8298 // Add the receiver (same as in function frame).
8299 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
8300 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
8301 Handle<Object> receiver(it.frame()->receiver());
8302 if (!receiver->IsJSObject()) {
8303 // If the receiver is NOT a JSObject we have hit an optimization
8304 // where a value object is not converted into a wrapped JS objects.
8305 // To hide this optimization from the debugger, we wrap the receiver
8306 // by creating correct wrapper object based on the calling frame's
8307 // global context.
8308 it.Advance();
8309 Handle<Context> calling_frames_global_context(
8310 Context::cast(Context::cast(it.frame()->context())->global_context()));
8311 receiver = Factory::ToObject(receiver, calling_frames_global_context);
8312 }
8313 details->set(kFrameDetailsReceiverIndex, *receiver);
8314
8315 ASSERT_EQ(details_size, details_index);
8316 return *Factory::NewJSArrayWithElements(details);
8317}
8318
8319
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008320// Copy all the context locals into an object used to materialize a scope.
8321static void CopyContextLocalsToScopeObject(Handle<Code> code,
8322 ScopeInfo<>& scope_info,
8323 Handle<Context> context,
8324 Handle<JSObject> scope_object) {
8325 // Fill all context locals to the context extension.
8326 for (int i = Context::MIN_CONTEXT_SLOTS;
8327 i < scope_info.number_of_context_slots();
8328 i++) {
8329 int context_index =
8330 ScopeInfo<>::ContextSlotIndex(*code,
8331 *scope_info.context_slot_name(i),
8332 NULL);
8333
8334 // Don't include the arguments shadow (.arguments) context variable.
8335 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
8336 SetProperty(scope_object,
8337 scope_info.context_slot_name(i),
8338 Handle<Object>(context->get(context_index)), NONE);
8339 }
8340 }
8341}
8342
8343
8344// Create a plain JSObject which materializes the local scope for the specified
8345// frame.
8346static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
8347 Handle<JSFunction> function(JSFunction::cast(frame->function()));
8348 Handle<Code> code(function->code());
8349 ScopeInfo<> scope_info(*code);
8350
8351 // Allocate and initialize a JSObject with all the arguments, stack locals
8352 // heap locals and extension properties of the debugged function.
8353 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
8354
8355 // First fill all parameters.
8356 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8357 SetProperty(local_scope,
8358 scope_info.parameter_name(i),
8359 Handle<Object>(frame->GetParameter(i)), NONE);
8360 }
8361
8362 // Second fill all stack locals.
8363 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
8364 SetProperty(local_scope,
8365 scope_info.stack_slot_name(i),
8366 Handle<Object>(frame->GetExpression(i)), NONE);
8367 }
8368
8369 // Third fill all context locals.
8370 Handle<Context> frame_context(Context::cast(frame->context()));
8371 Handle<Context> function_context(frame_context->fcontext());
8372 CopyContextLocalsToScopeObject(code, scope_info,
8373 function_context, local_scope);
8374
8375 // Finally copy any properties from the function context extension. This will
8376 // be variables introduced by eval.
8377 if (function_context->closure() == *function) {
8378 if (function_context->has_extension() &&
8379 !function_context->IsGlobalContext()) {
8380 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008381 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008382 for (int i = 0; i < keys->length(); i++) {
8383 // Names of variables introduced by eval are strings.
8384 ASSERT(keys->get(i)->IsString());
8385 Handle<String> key(String::cast(keys->get(i)));
8386 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
8387 }
8388 }
8389 }
8390 return local_scope;
8391}
8392
8393
8394// Create a plain JSObject which materializes the closure content for the
8395// context.
8396static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
8397 ASSERT(context->is_function_context());
8398
8399 Handle<Code> code(context->closure()->code());
8400 ScopeInfo<> scope_info(*code);
8401
8402 // Allocate and initialize a JSObject with all the content of theis function
8403 // closure.
8404 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
8405
8406 // Check whether the arguments shadow object exists.
8407 int arguments_shadow_index =
8408 ScopeInfo<>::ContextSlotIndex(*code,
8409 Heap::arguments_shadow_symbol(),
8410 NULL);
8411 if (arguments_shadow_index >= 0) {
8412 // In this case all the arguments are available in the arguments shadow
8413 // object.
8414 Handle<JSObject> arguments_shadow(
8415 JSObject::cast(context->get(arguments_shadow_index)));
8416 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
8417 SetProperty(closure_scope,
8418 scope_info.parameter_name(i),
8419 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
8420 }
8421 }
8422
8423 // Fill all context locals to the context extension.
8424 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
8425
8426 // Finally copy any properties from the function context extension. This will
8427 // be variables introduced by eval.
8428 if (context->has_extension()) {
8429 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00008430 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008431 for (int i = 0; i < keys->length(); i++) {
8432 // Names of variables introduced by eval are strings.
8433 ASSERT(keys->get(i)->IsString());
8434 Handle<String> key(String::cast(keys->get(i)));
8435 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
8436 }
8437 }
8438
8439 return closure_scope;
8440}
8441
8442
8443// Iterate over the actual scopes visible from a stack frame. All scopes are
8444// backed by an actual context except the local scope, which is inserted
8445// "artifically" in the context chain.
8446class ScopeIterator {
8447 public:
8448 enum ScopeType {
8449 ScopeTypeGlobal = 0,
8450 ScopeTypeLocal,
8451 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00008452 ScopeTypeClosure,
8453 // Every catch block contains an implicit with block (its parameter is
8454 // a JSContextExtensionObject) that extends current scope with a variable
8455 // holding exception object. Such with blocks are treated as scopes of their
8456 // own type.
8457 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008458 };
8459
8460 explicit ScopeIterator(JavaScriptFrame* frame)
8461 : frame_(frame),
8462 function_(JSFunction::cast(frame->function())),
8463 context_(Context::cast(frame->context())),
8464 local_done_(false),
8465 at_local_(false) {
8466
8467 // Check whether the first scope is actually a local scope.
8468 if (context_->IsGlobalContext()) {
8469 // If there is a stack slot for .result then this local scope has been
8470 // created for evaluating top level code and it is not a real local scope.
8471 // Checking for the existence of .result seems fragile, but the scope info
8472 // saved with the code object does not otherwise have that information.
8473 Handle<Code> code(function_->code());
8474 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
8475 at_local_ = index < 0;
8476 } else if (context_->is_function_context()) {
8477 at_local_ = true;
8478 }
8479 }
8480
8481 // More scopes?
8482 bool Done() { return context_.is_null(); }
8483
8484 // Move to the next scope.
8485 void Next() {
8486 // If at a local scope mark the local scope as passed.
8487 if (at_local_) {
8488 at_local_ = false;
8489 local_done_ = true;
8490
8491 // If the current context is not associated with the local scope the
8492 // current context is the next real scope, so don't move to the next
8493 // context in this case.
8494 if (context_->closure() != *function_) {
8495 return;
8496 }
8497 }
8498
8499 // The global scope is always the last in the chain.
8500 if (context_->IsGlobalContext()) {
8501 context_ = Handle<Context>();
8502 return;
8503 }
8504
8505 // Move to the next context.
8506 if (context_->is_function_context()) {
8507 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
8508 } else {
8509 context_ = Handle<Context>(context_->previous());
8510 }
8511
8512 // If passing the local scope indicate that the current scope is now the
8513 // local scope.
8514 if (!local_done_ &&
8515 (context_->IsGlobalContext() || (context_->is_function_context()))) {
8516 at_local_ = true;
8517 }
8518 }
8519
8520 // Return the type of the current scope.
8521 int Type() {
8522 if (at_local_) {
8523 return ScopeTypeLocal;
8524 }
8525 if (context_->IsGlobalContext()) {
8526 ASSERT(context_->global()->IsGlobalObject());
8527 return ScopeTypeGlobal;
8528 }
8529 if (context_->is_function_context()) {
8530 return ScopeTypeClosure;
8531 }
8532 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00008533 // Current scope is either an explicit with statement or a with statement
8534 // implicitely generated for a catch block.
8535 // If the extension object here is a JSContextExtensionObject then
8536 // current with statement is one frome a catch block otherwise it's a
8537 // regular with statement.
8538 if (context_->extension()->IsJSContextExtensionObject()) {
8539 return ScopeTypeCatch;
8540 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008541 return ScopeTypeWith;
8542 }
8543
8544 // Return the JavaScript object with the content of the current scope.
8545 Handle<JSObject> ScopeObject() {
8546 switch (Type()) {
8547 case ScopeIterator::ScopeTypeGlobal:
8548 return Handle<JSObject>(CurrentContext()->global());
8549 break;
8550 case ScopeIterator::ScopeTypeLocal:
8551 // Materialize the content of the local scope into a JSObject.
8552 return MaterializeLocalScope(frame_);
8553 break;
8554 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00008555 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008556 // Return the with object.
8557 return Handle<JSObject>(CurrentContext()->extension());
8558 break;
8559 case ScopeIterator::ScopeTypeClosure:
8560 // Materialize the content of the closure scope into a JSObject.
8561 return MaterializeClosure(CurrentContext());
8562 break;
8563 }
8564 UNREACHABLE();
8565 return Handle<JSObject>();
8566 }
8567
8568 // Return the context for this scope. For the local context there might not
8569 // be an actual context.
8570 Handle<Context> CurrentContext() {
8571 if (at_local_ && context_->closure() != *function_) {
8572 return Handle<Context>();
8573 }
8574 return context_;
8575 }
8576
8577#ifdef DEBUG
8578 // Debug print of the content of the current scope.
8579 void DebugPrint() {
8580 switch (Type()) {
8581 case ScopeIterator::ScopeTypeGlobal:
8582 PrintF("Global:\n");
8583 CurrentContext()->Print();
8584 break;
8585
8586 case ScopeIterator::ScopeTypeLocal: {
8587 PrintF("Local:\n");
8588 Handle<Code> code(function_->code());
8589 ScopeInfo<> scope_info(*code);
8590 scope_info.Print();
8591 if (!CurrentContext().is_null()) {
8592 CurrentContext()->Print();
8593 if (CurrentContext()->has_extension()) {
8594 Handle<JSObject> extension =
8595 Handle<JSObject>(CurrentContext()->extension());
8596 if (extension->IsJSContextExtensionObject()) {
8597 extension->Print();
8598 }
8599 }
8600 }
8601 break;
8602 }
8603
8604 case ScopeIterator::ScopeTypeWith: {
8605 PrintF("With:\n");
8606 Handle<JSObject> extension =
8607 Handle<JSObject>(CurrentContext()->extension());
8608 extension->Print();
8609 break;
8610 }
8611
ager@chromium.orga1645e22009-09-09 19:27:10 +00008612 case ScopeIterator::ScopeTypeCatch: {
8613 PrintF("Catch:\n");
8614 Handle<JSObject> extension =
8615 Handle<JSObject>(CurrentContext()->extension());
8616 extension->Print();
8617 break;
8618 }
8619
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008620 case ScopeIterator::ScopeTypeClosure: {
8621 PrintF("Closure:\n");
8622 CurrentContext()->Print();
8623 if (CurrentContext()->has_extension()) {
8624 Handle<JSObject> extension =
8625 Handle<JSObject>(CurrentContext()->extension());
8626 if (extension->IsJSContextExtensionObject()) {
8627 extension->Print();
8628 }
8629 }
8630 break;
8631 }
8632
8633 default:
8634 UNREACHABLE();
8635 }
8636 PrintF("\n");
8637 }
8638#endif
8639
8640 private:
8641 JavaScriptFrame* frame_;
8642 Handle<JSFunction> function_;
8643 Handle<Context> context_;
8644 bool local_done_;
8645 bool at_local_;
8646
8647 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
8648};
8649
8650
8651static Object* Runtime_GetScopeCount(Arguments args) {
8652 HandleScope scope;
8653 ASSERT(args.length() == 2);
8654
8655 // Check arguments.
8656 Object* check = Runtime_CheckExecutionState(args);
8657 if (check->IsFailure()) return check;
8658 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8659
8660 // Get the frame where the debugging is performed.
8661 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8662 JavaScriptFrameIterator it(id);
8663 JavaScriptFrame* frame = it.frame();
8664
8665 // Count the visible scopes.
8666 int n = 0;
8667 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8668 n++;
8669 }
8670
8671 return Smi::FromInt(n);
8672}
8673
8674
8675static const int kScopeDetailsTypeIndex = 0;
8676static const int kScopeDetailsObjectIndex = 1;
8677static const int kScopeDetailsSize = 2;
8678
8679// Return an array with scope details
8680// args[0]: number: break id
8681// args[1]: number: frame index
8682// args[2]: number: scope index
8683//
8684// The array returned contains the following information:
8685// 0: Scope type
8686// 1: Scope object
8687static Object* Runtime_GetScopeDetails(Arguments args) {
8688 HandleScope scope;
8689 ASSERT(args.length() == 3);
8690
8691 // Check arguments.
8692 Object* check = Runtime_CheckExecutionState(args);
8693 if (check->IsFailure()) return check;
8694 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
8695 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
8696
8697 // Get the frame where the debugging is performed.
8698 StackFrame::Id id = UnwrapFrameId(wrapped_id);
8699 JavaScriptFrameIterator frame_it(id);
8700 JavaScriptFrame* frame = frame_it.frame();
8701
8702 // Find the requested scope.
8703 int n = 0;
8704 ScopeIterator it(frame);
8705 for (; !it.Done() && n < index; it.Next()) {
8706 n++;
8707 }
8708 if (it.Done()) {
8709 return Heap::undefined_value();
8710 }
8711
8712 // Calculate the size of the result.
8713 int details_size = kScopeDetailsSize;
8714 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
8715
8716 // Fill in scope details.
8717 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
8718 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
8719
8720 return *Factory::NewJSArrayWithElements(details);
8721}
8722
8723
8724static Object* Runtime_DebugPrintScopes(Arguments args) {
8725 HandleScope scope;
8726 ASSERT(args.length() == 0);
8727
8728#ifdef DEBUG
8729 // Print the scopes for the top frame.
8730 StackFrameLocator locator;
8731 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
8732 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
8733 it.DebugPrint();
8734 }
8735#endif
8736 return Heap::undefined_value();
8737}
8738
8739
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008740static Object* Runtime_GetCFrames(Arguments args) {
8741 HandleScope scope;
8742 ASSERT(args.length() == 1);
8743 Object* result = Runtime_CheckExecutionState(args);
8744 if (result->IsFailure()) return result;
8745
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008746#if V8_HOST_ARCH_64_BIT
8747 UNIMPLEMENTED();
8748 return Heap::undefined_value();
8749#else
8750
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008751 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008752 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
8753 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008754 if (frames_count == OS::kStackWalkError) {
8755 return Heap::undefined_value();
8756 }
8757
8758 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
8759 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
8760 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
8761 for (int i = 0; i < frames_count; i++) {
8762 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
8763 frame_value->SetProperty(
8764 *address_str,
8765 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
8766 NONE);
8767
8768 // Get the stack walk text for this frame.
8769 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00008770 int frame_text_length = StrLength(frames[i].text);
8771 if (frame_text_length > 0) {
8772 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008773 frame_text = Factory::NewStringFromAscii(str);
8774 }
8775
8776 if (!frame_text.is_null()) {
8777 frame_value->SetProperty(*text_str, *frame_text, NONE);
8778 }
8779
8780 frames_array->set(i, *frame_value);
8781 }
8782 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00008783#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008784}
8785
8786
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00008787static Object* Runtime_GetThreadCount(Arguments args) {
8788 HandleScope scope;
8789 ASSERT(args.length() == 1);
8790
8791 // Check arguments.
8792 Object* result = Runtime_CheckExecutionState(args);
8793 if (result->IsFailure()) return result;
8794
8795 // Count all archived V8 threads.
8796 int n = 0;
8797 for (ThreadState* thread = ThreadState::FirstInUse();
8798 thread != NULL;
8799 thread = thread->Next()) {
8800 n++;
8801 }
8802
8803 // Total number of threads is current thread and archived threads.
8804 return Smi::FromInt(n + 1);
8805}
8806
8807
8808static const int kThreadDetailsCurrentThreadIndex = 0;
8809static const int kThreadDetailsThreadIdIndex = 1;
8810static const int kThreadDetailsSize = 2;
8811
8812// Return an array with thread details
8813// args[0]: number: break id
8814// args[1]: number: thread index
8815//
8816// The array returned contains the following information:
8817// 0: Is current thread?
8818// 1: Thread id
8819static Object* Runtime_GetThreadDetails(Arguments args) {
8820 HandleScope scope;
8821 ASSERT(args.length() == 2);
8822
8823 // Check arguments.
8824 Object* check = Runtime_CheckExecutionState(args);
8825 if (check->IsFailure()) return check;
8826 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
8827
8828 // Allocate array for result.
8829 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
8830
8831 // Thread index 0 is current thread.
8832 if (index == 0) {
8833 // Fill the details.
8834 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
8835 details->set(kThreadDetailsThreadIdIndex,
8836 Smi::FromInt(ThreadManager::CurrentId()));
8837 } else {
8838 // Find the thread with the requested index.
8839 int n = 1;
8840 ThreadState* thread = ThreadState::FirstInUse();
8841 while (index != n && thread != NULL) {
8842 thread = thread->Next();
8843 n++;
8844 }
8845 if (thread == NULL) {
8846 return Heap::undefined_value();
8847 }
8848
8849 // Fill the details.
8850 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
8851 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
8852 }
8853
8854 // Convert to JS array and return.
8855 return *Factory::NewJSArrayWithElements(details);
8856}
8857
8858
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008859static Object* Runtime_GetBreakLocations(Arguments args) {
8860 HandleScope scope;
8861 ASSERT(args.length() == 1);
8862
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008863 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8864 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008865 // Find the number of break points
8866 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
8867 if (break_locations->IsUndefined()) return Heap::undefined_value();
8868 // Return array as JS array
8869 return *Factory::NewJSArrayWithElements(
8870 Handle<FixedArray>::cast(break_locations));
8871}
8872
8873
8874// Set a break point in a function
8875// args[0]: function
8876// args[1]: number: break source position (within the function source)
8877// args[2]: number: break point object
8878static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
8879 HandleScope scope;
8880 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00008881 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
8882 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008883 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8884 RUNTIME_ASSERT(source_position >= 0);
8885 Handle<Object> break_point_object_arg = args.at<Object>(2);
8886
8887 // Set break point.
8888 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
8889
8890 return Heap::undefined_value();
8891}
8892
8893
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00008894Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
8895 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008896 // Iterate the heap looking for SharedFunctionInfo generated from the
8897 // script. The inner most SharedFunctionInfo containing the source position
8898 // for the requested break point is found.
8899 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
8900 // which is found is not compiled it is compiled and the heap is iterated
8901 // again as the compilation might create inner functions from the newly
8902 // compiled function and the actual requested break point might be in one of
8903 // these functions.
8904 bool done = false;
8905 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00008906 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008907 Handle<SharedFunctionInfo> target;
8908 // The current candidate for the last function in script:
8909 Handle<SharedFunctionInfo> last;
8910 while (!done) {
8911 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008912 for (HeapObject* obj = iterator.next();
8913 obj != NULL; obj = iterator.next()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008914 if (obj->IsSharedFunctionInfo()) {
8915 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
8916 if (shared->script() == *script) {
8917 // If the SharedFunctionInfo found has the requested script data and
8918 // contains the source position it is a candidate.
8919 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00008920 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008921 start_position = shared->start_position();
8922 }
8923 if (start_position <= position &&
8924 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00008925 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008926 // candidate this is the new candidate.
8927 if (target.is_null()) {
8928 target_start_position = start_position;
8929 target = shared;
8930 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00008931 if (target_start_position == start_position &&
8932 shared->end_position() == target->end_position()) {
8933 // If a top-level function contain only one function
8934 // declartion the source for the top-level and the function is
8935 // the same. In that case prefer the non top-level function.
8936 if (!shared->is_toplevel()) {
8937 target_start_position = start_position;
8938 target = shared;
8939 }
8940 } else if (target_start_position <= start_position &&
8941 shared->end_position() <= target->end_position()) {
8942 // This containment check includes equality as a function inside
8943 // a top-level function can share either start or end position
8944 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008945 target_start_position = start_position;
8946 target = shared;
8947 }
8948 }
8949 }
8950
8951 // Keep track of the last function in the script.
8952 if (last.is_null() ||
8953 shared->end_position() > last->start_position()) {
8954 last = shared;
8955 }
8956 }
8957 }
8958 }
8959
8960 // Make sure some candidate is selected.
8961 if (target.is_null()) {
8962 if (!last.is_null()) {
8963 // Position after the last function - use last.
8964 target = last;
8965 } else {
8966 // Unable to find function - possibly script without any function.
8967 return Heap::undefined_value();
8968 }
8969 }
8970
8971 // If the candidate found is compiled we are done. NOTE: when lazy
8972 // compilation of inner functions is introduced some additional checking
8973 // needs to be done here to compile inner functions.
8974 done = target->is_compiled();
8975 if (!done) {
8976 // If the candidate is not compiled compile it to reveal any inner
8977 // functions which might contain the requested source position.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008978 CompileLazyShared(target, KEEP_EXCEPTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00008979 }
8980 }
8981
8982 return *target;
8983}
8984
8985
8986// Change the state of a break point in a script. NOTE: Regarding performance
8987// see the NOTE for GetScriptFromScriptData.
8988// args[0]: script to set break point in
8989// args[1]: number: break source position (within the script source)
8990// args[2]: number: break point object
8991static Object* Runtime_SetScriptBreakPoint(Arguments args) {
8992 HandleScope scope;
8993 ASSERT(args.length() == 3);
8994 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
8995 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
8996 RUNTIME_ASSERT(source_position >= 0);
8997 Handle<Object> break_point_object_arg = args.at<Object>(2);
8998
8999 // Get the script from the script wrapper.
9000 RUNTIME_ASSERT(wrapper->value()->IsScript());
9001 Handle<Script> script(Script::cast(wrapper->value()));
9002
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00009003 Object* result = Runtime::FindSharedFunctionInfoInScript(
9004 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009005 if (!result->IsUndefined()) {
9006 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
9007 // Find position within function. The script position might be before the
9008 // source position of the first function.
9009 int position;
9010 if (shared->start_position() > source_position) {
9011 position = 0;
9012 } else {
9013 position = source_position - shared->start_position();
9014 }
9015 Debug::SetBreakPoint(shared, position, break_point_object_arg);
9016 }
9017 return Heap::undefined_value();
9018}
9019
9020
9021// Clear a break point
9022// args[0]: number: break point object
9023static Object* Runtime_ClearBreakPoint(Arguments args) {
9024 HandleScope scope;
9025 ASSERT(args.length() == 1);
9026 Handle<Object> break_point_object_arg = args.at<Object>(0);
9027
9028 // Clear break point.
9029 Debug::ClearBreakPoint(break_point_object_arg);
9030
9031 return Heap::undefined_value();
9032}
9033
9034
9035// Change the state of break on exceptions
9036// args[0]: boolean indicating uncaught exceptions
9037// args[1]: boolean indicating on/off
9038static Object* Runtime_ChangeBreakOnException(Arguments args) {
9039 HandleScope scope;
9040 ASSERT(args.length() == 2);
9041 ASSERT(args[0]->IsNumber());
9042 ASSERT(args[1]->IsBoolean());
9043
9044 // Update break point state
9045 ExceptionBreakType type =
9046 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
9047 bool enable = args[1]->ToBoolean()->IsTrue();
9048 Debug::ChangeBreakOnException(type, enable);
9049 return Heap::undefined_value();
9050}
9051
9052
9053// Prepare for stepping
9054// args[0]: break id for checking execution state
9055// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00009056// args[2]: number of times to perform the step, for step out it is the number
9057// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009058static Object* Runtime_PrepareStep(Arguments args) {
9059 HandleScope scope;
9060 ASSERT(args.length() == 3);
9061 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00009062 Object* check = Runtime_CheckExecutionState(args);
9063 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009064 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
9065 return Top::Throw(Heap::illegal_argument_symbol());
9066 }
9067
9068 // Get the step action and check validity.
9069 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
9070 if (step_action != StepIn &&
9071 step_action != StepNext &&
9072 step_action != StepOut &&
9073 step_action != StepInMin &&
9074 step_action != StepMin) {
9075 return Top::Throw(Heap::illegal_argument_symbol());
9076 }
9077
9078 // Get the number of steps.
9079 int step_count = NumberToInt32(args[2]);
9080 if (step_count < 1) {
9081 return Top::Throw(Heap::illegal_argument_symbol());
9082 }
9083
ager@chromium.orga1645e22009-09-09 19:27:10 +00009084 // Clear all current stepping setup.
9085 Debug::ClearStepping();
9086
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009087 // Prepare step.
9088 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
9089 return Heap::undefined_value();
9090}
9091
9092
9093// Clear all stepping set by PrepareStep.
9094static Object* Runtime_ClearStepping(Arguments args) {
9095 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009096 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009097 Debug::ClearStepping();
9098 return Heap::undefined_value();
9099}
9100
9101
9102// Creates a copy of the with context chain. The copy of the context chain is
9103// is linked to the function context supplied.
9104static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
9105 Handle<Context> function_context) {
9106 // At the bottom of the chain. Return the function context to link to.
9107 if (context_chain->is_function_context()) {
9108 return function_context;
9109 }
9110
9111 // Recursively copy the with contexts.
9112 Handle<Context> previous(context_chain->previous());
9113 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
9114 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00009115 CopyWithContextChain(function_context, previous),
9116 extension,
9117 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009118}
9119
9120
9121// Helper function to find or create the arguments object for
9122// Runtime_DebugEvaluate.
9123static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
9124 Handle<JSFunction> function,
9125 Handle<Code> code,
9126 const ScopeInfo<>* sinfo,
9127 Handle<Context> function_context) {
9128 // Try to find the value of 'arguments' to pass as parameter. If it is not
9129 // found (that is the debugged function does not reference 'arguments' and
9130 // does not support eval) then create an 'arguments' object.
9131 int index;
9132 if (sinfo->number_of_stack_slots() > 0) {
9133 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
9134 if (index != -1) {
9135 return Handle<Object>(frame->GetExpression(index));
9136 }
9137 }
9138
9139 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
9140 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
9141 NULL);
9142 if (index != -1) {
9143 return Handle<Object>(function_context->get(index));
9144 }
9145 }
9146
9147 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009148 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
9149 Handle<FixedArray> array = Factory::NewFixedArray(length);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009150
9151 AssertNoAllocation no_gc;
9152 WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009153 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00009154 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009155 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00009156 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009157 return arguments;
9158}
9159
9160
9161// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00009162// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009163// extension part has all the parameters and locals of the function on the
9164// stack frame. A function which calls eval with the code to evaluate is then
9165// compiled in this context and called in this context. As this context
9166// replaces the context of the function on the stack frame a new (empty)
9167// function is created as well to be used as the closure for the context.
9168// This function and the context acts as replacements for the function on the
9169// stack frame presenting the same view of the values of parameters and
9170// local variables as if the piece of JavaScript was evaluated at the point
9171// where the function on the stack frame is currently stopped.
9172static Object* Runtime_DebugEvaluate(Arguments args) {
9173 HandleScope scope;
9174
9175 // Check the execution state and decode arguments frame and source to be
9176 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009177 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009178 Object* check_result = Runtime_CheckExecutionState(args);
9179 if (check_result->IsFailure()) return check_result;
9180 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
9181 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009182 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
9183
9184 // Handle the processing of break.
9185 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009186
9187 // Get the frame where the debugging is performed.
9188 StackFrame::Id id = UnwrapFrameId(wrapped_id);
9189 JavaScriptFrameIterator it(id);
9190 JavaScriptFrame* frame = it.frame();
9191 Handle<JSFunction> function(JSFunction::cast(frame->function()));
9192 Handle<Code> code(function->code());
9193 ScopeInfo<> sinfo(*code);
9194
9195 // Traverse the saved contexts chain to find the active context for the
9196 // selected frame.
9197 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00009198 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009199 save = save->prev();
9200 }
9201 ASSERT(save != NULL);
9202 SaveContext savex;
9203 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009204
9205 // Create the (empty) function replacing the function on the stack frame for
9206 // the purpose of evaluating in the context created below. It is important
9207 // that this function does not describe any parameters and local variables
9208 // in the context. If it does then this will cause problems with the lookup
9209 // in Context::Lookup, where context slots for parameters and local variables
9210 // are looked at before the extension object.
9211 Handle<JSFunction> go_between =
9212 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
9213 go_between->set_context(function->context());
9214#ifdef DEBUG
9215 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
9216 ASSERT(go_between_sinfo.number_of_parameters() == 0);
9217 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
9218#endif
9219
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009220 // Materialize the content of the local scope into a JSObject.
9221 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009222
9223 // Allocate a new context for the debug evaluation and set the extension
9224 // object build.
9225 Handle<Context> context =
9226 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009227 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009228 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009229 Handle<Context> frame_context(Context::cast(frame->context()));
9230 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009231 context = CopyWithContextChain(frame_context, context);
9232
9233 // Wrap the evaluation statement in a new function compiled in the newly
9234 // created context. The function has one parameter which has to be called
9235 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00009236 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009237 // function(arguments,__source__) {return eval(__source__);}
9238 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00009239 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009240 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009241 Handle<String> function_source =
9242 Factory::NewStringFromAscii(Vector<const char>(source_str,
9243 source_str_length));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009244 Handle<SharedFunctionInfo> shared =
ager@chromium.org381abbb2009-02-25 13:23:22 +00009245 Compiler::CompileEval(function_source,
9246 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00009247 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00009248 Compiler::DONT_VALIDATE_JSON);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009249 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009250 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009251 Factory::NewFunctionFromSharedFunctionInfo(shared, context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009252
9253 // Invoke the result of the compilation to get the evaluation function.
9254 bool has_pending_exception;
9255 Handle<Object> receiver(frame->receiver());
9256 Handle<Object> evaluation_function =
9257 Execution::Call(compiled_function, receiver, 0, NULL,
9258 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009259 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009260
9261 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
9262 function_context);
9263
9264 // Invoke the evaluation function and return the result.
9265 const int argc = 2;
9266 Object** argv[argc] = { arguments.location(),
9267 Handle<Object>::cast(source).location() };
9268 Handle<Object> result =
9269 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
9270 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009271 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009272
9273 // Skip the global proxy as it has no properties and always delegates to the
9274 // real global object.
9275 if (result->IsJSGlobalProxy()) {
9276 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
9277 }
9278
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009279 return *result;
9280}
9281
9282
9283static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
9284 HandleScope scope;
9285
9286 // Check the execution state and decode arguments frame and source to be
9287 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009288 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009289 Object* check_result = Runtime_CheckExecutionState(args);
9290 if (check_result->IsFailure()) return check_result;
9291 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00009292 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
9293
9294 // Handle the processing of break.
9295 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009296
9297 // Enter the top context from before the debugger was invoked.
9298 SaveContext save;
9299 SaveContext* top = &save;
9300 while (top != NULL && *top->context() == *Debug::debug_context()) {
9301 top = top->prev();
9302 }
9303 if (top != NULL) {
9304 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009305 }
9306
9307 // Get the global context now set to the top context from before the
9308 // debugger was invoked.
9309 Handle<Context> context = Top::global_context();
9310
9311 // Compile the source to be evaluated.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009312 Handle<SharedFunctionInfo> shared =
9313 Compiler::CompileEval(source,
9314 context,
9315 true,
9316 Compiler::DONT_VALIDATE_JSON);
9317 if (shared.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009318 Handle<JSFunction> compiled_function =
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009319 Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
9320 context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009321
9322 // Invoke the result of the compilation to get the evaluation function.
9323 bool has_pending_exception;
9324 Handle<Object> receiver = Top::global();
9325 Handle<Object> result =
9326 Execution::Call(compiled_function, receiver, 0, NULL,
9327 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009328 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009329 return *result;
9330}
9331
9332
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009333static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
9334 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00009335 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009336
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009337 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009338 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009339
9340 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00009341 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00009342 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
9343 // Get the script wrapper in a local handle before calling GetScriptWrapper,
9344 // because using
9345 // instances->set(i, *GetScriptWrapper(script))
9346 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
9347 // already have deferenced the instances handle.
9348 Handle<JSValue> wrapper = GetScriptWrapper(script);
9349 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009350 }
9351
9352 // Return result as a JS array.
9353 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
9354 Handle<JSArray>::cast(result)->SetContent(*instances);
9355 return *result;
9356}
9357
9358
9359// Helper function used by Runtime_DebugReferencedBy below.
9360static int DebugReferencedBy(JSObject* target,
9361 Object* instance_filter, int max_references,
9362 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009363 JSFunction* arguments_function) {
9364 NoHandleAllocation ha;
9365 AssertNoAllocation no_alloc;
9366
9367 // Iterate the heap.
9368 int count = 0;
9369 JSObject* last = NULL;
9370 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009371 HeapObject* heap_obj = NULL;
9372 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009373 (max_references == 0 || count < max_references)) {
9374 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009375 if (heap_obj->IsJSObject()) {
9376 // Skip context extension objects and argument arrays as these are
9377 // checked in the context of functions using them.
9378 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00009379 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009380 obj->map()->constructor() == arguments_function) {
9381 continue;
9382 }
9383
9384 // Check if the JS object has a reference to the object looked for.
9385 if (obj->ReferencesObject(target)) {
9386 // Check instance filter if supplied. This is normally used to avoid
9387 // references from mirror objects (see Runtime_IsInPrototypeChain).
9388 if (!instance_filter->IsUndefined()) {
9389 Object* V = obj;
9390 while (true) {
9391 Object* prototype = V->GetPrototype();
9392 if (prototype->IsNull()) {
9393 break;
9394 }
9395 if (instance_filter == prototype) {
9396 obj = NULL; // Don't add this object.
9397 break;
9398 }
9399 V = prototype;
9400 }
9401 }
9402
9403 if (obj != NULL) {
9404 // Valid reference found add to instance array if supplied an update
9405 // count.
9406 if (instances != NULL && count < instances_size) {
9407 instances->set(count, obj);
9408 }
9409 last = obj;
9410 count++;
9411 }
9412 }
9413 }
9414 }
9415
9416 // Check for circular reference only. This can happen when the object is only
9417 // referenced from mirrors and has a circular reference in which case the
9418 // object is not really alive and would have been garbage collected if not
9419 // referenced from the mirror.
9420 if (count == 1 && last == target) {
9421 count = 0;
9422 }
9423
9424 // Return the number of referencing objects found.
9425 return count;
9426}
9427
9428
9429// Scan the heap for objects with direct references to an object
9430// args[0]: the object to find references to
9431// args[1]: constructor function for instances to exclude (Mirror)
9432// args[2]: the the maximum number of objects to return
9433static Object* Runtime_DebugReferencedBy(Arguments args) {
9434 ASSERT(args.length() == 3);
9435
9436 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009437 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009438
9439 // Check parameters.
9440 CONVERT_CHECKED(JSObject, target, args[0]);
9441 Object* instance_filter = args[1];
9442 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
9443 instance_filter->IsJSObject());
9444 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
9445 RUNTIME_ASSERT(max_references >= 0);
9446
9447 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009448 JSObject* arguments_boilerplate =
9449 Top::context()->global_context()->arguments_boilerplate();
9450 JSFunction* arguments_function =
9451 JSFunction::cast(arguments_boilerplate->map()->constructor());
9452
9453 // Get the number of referencing objects.
9454 int count;
9455 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009456 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009457
9458 // Allocate an array to hold the result.
9459 Object* object = Heap::AllocateFixedArray(count);
9460 if (object->IsFailure()) return object;
9461 FixedArray* instances = FixedArray::cast(object);
9462
9463 // Fill the referencing objects.
9464 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00009465 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009466
9467 // Return result as JS array.
9468 Object* result =
9469 Heap::AllocateJSObject(
9470 Top::context()->global_context()->array_function());
9471 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9472 return result;
9473}
9474
9475
9476// Helper function used by Runtime_DebugConstructedBy below.
9477static int DebugConstructedBy(JSFunction* constructor, int max_references,
9478 FixedArray* instances, int instances_size) {
9479 AssertNoAllocation no_alloc;
9480
9481 // Iterate the heap.
9482 int count = 0;
9483 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009484 HeapObject* heap_obj = NULL;
9485 while (((heap_obj = iterator.next()) != NULL) &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009486 (max_references == 0 || count < max_references)) {
9487 // Only look at all JSObjects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009488 if (heap_obj->IsJSObject()) {
9489 JSObject* obj = JSObject::cast(heap_obj);
9490 if (obj->map()->constructor() == constructor) {
9491 // Valid reference found add to instance array if supplied an update
9492 // count.
9493 if (instances != NULL && count < instances_size) {
9494 instances->set(count, obj);
9495 }
9496 count++;
9497 }
9498 }
9499 }
9500
9501 // Return the number of referencing objects found.
9502 return count;
9503}
9504
9505
9506// Scan the heap for objects constructed by a specific function.
9507// args[0]: the constructor to find instances of
9508// args[1]: the the maximum number of objects to return
9509static Object* Runtime_DebugConstructedBy(Arguments args) {
9510 ASSERT(args.length() == 2);
9511
9512 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00009513 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009514
9515 // Check parameters.
9516 CONVERT_CHECKED(JSFunction, constructor, args[0]);
9517 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
9518 RUNTIME_ASSERT(max_references >= 0);
9519
9520 // Get the number of referencing objects.
9521 int count;
9522 count = DebugConstructedBy(constructor, max_references, NULL, 0);
9523
9524 // Allocate an array to hold the result.
9525 Object* object = Heap::AllocateFixedArray(count);
9526 if (object->IsFailure()) return object;
9527 FixedArray* instances = FixedArray::cast(object);
9528
9529 // Fill the referencing objects.
9530 count = DebugConstructedBy(constructor, max_references, instances, count);
9531
9532 // Return result as JS array.
9533 Object* result =
9534 Heap::AllocateJSObject(
9535 Top::context()->global_context()->array_function());
9536 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
9537 return result;
9538}
9539
9540
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009541// Find the effective prototype object as returned by __proto__.
9542// args[0]: the object to find the prototype for.
9543static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009544 ASSERT(args.length() == 1);
9545
9546 CONVERT_CHECKED(JSObject, obj, args[0]);
9547
ager@chromium.orgddb913d2009-01-27 10:01:48 +00009548 // Use the __proto__ accessor.
9549 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009550}
9551
9552
9553static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00009554 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009555 CPU::DebugBreak();
9556 return Heap::undefined_value();
9557}
9558
9559
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009560static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009561#ifdef DEBUG
9562 HandleScope scope;
9563 ASSERT(args.length() == 1);
9564 // Get the function and make sure it is compiled.
9565 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009566 Handle<SharedFunctionInfo> shared(func->shared());
9567 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009568 return Failure::Exception();
9569 }
9570 func->code()->PrintLn();
9571#endif // DEBUG
9572 return Heap::undefined_value();
9573}
ager@chromium.org9085a012009-05-11 19:22:57 +00009574
9575
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009576static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
9577#ifdef DEBUG
9578 HandleScope scope;
9579 ASSERT(args.length() == 1);
9580 // Get the function and make sure it is compiled.
9581 CONVERT_ARG_CHECKED(JSFunction, func, 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009582 Handle<SharedFunctionInfo> shared(func->shared());
9583 if (!EnsureCompiled(shared, KEEP_EXCEPTION)) {
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009584 return Failure::Exception();
9585 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009586 shared->construct_stub()->PrintLn();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00009587#endif // DEBUG
9588 return Heap::undefined_value();
9589}
9590
9591
ager@chromium.org9085a012009-05-11 19:22:57 +00009592static Object* Runtime_FunctionGetInferredName(Arguments args) {
9593 NoHandleAllocation ha;
9594 ASSERT(args.length() == 1);
9595
9596 CONVERT_CHECKED(JSFunction, f, args[0]);
9597 return f->shared()->inferred_name();
9598}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009599
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009600
9601static int FindSharedFunctionInfosForScript(Script* script,
9602 FixedArray* buffer) {
9603 AssertNoAllocation no_allocations;
9604
9605 int counter = 0;
9606 int buffer_size = buffer->length();
9607 HeapIterator iterator;
9608 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
9609 ASSERT(obj != NULL);
9610 if (!obj->IsSharedFunctionInfo()) {
9611 continue;
9612 }
9613 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
9614 if (shared->script() != script) {
9615 continue;
9616 }
9617 if (counter < buffer_size) {
9618 buffer->set(counter, shared);
9619 }
9620 counter++;
9621 }
9622 return counter;
9623}
9624
9625// For a script finds all SharedFunctionInfo's in the heap that points
9626// to this script. Returns JSArray of SharedFunctionInfo wrapped
9627// in OpaqueReferences.
9628static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
9629 Arguments args) {
9630 ASSERT(args.length() == 1);
9631 HandleScope scope;
9632 CONVERT_CHECKED(JSValue, script_value, args[0]);
9633
9634 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9635
9636 const int kBufferSize = 32;
9637
9638 Handle<FixedArray> array;
9639 array = Factory::NewFixedArray(kBufferSize);
9640 int number = FindSharedFunctionInfosForScript(*script, *array);
9641 if (number > kBufferSize) {
9642 array = Factory::NewFixedArray(number);
9643 FindSharedFunctionInfosForScript(*script, *array);
9644 }
9645
9646 Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
9647 result->set_length(Smi::FromInt(number));
9648
9649 LiveEdit::WrapSharedFunctionInfos(result);
9650
9651 return *result;
9652}
9653
9654// For a script calculates compilation information about all its functions.
9655// The script source is explicitly specified by the second argument.
9656// The source of the actual script is not used, however it is important that
9657// all generated code keeps references to this particular instance of script.
9658// Returns a JSArray of compilation infos. The array is ordered so that
9659// each function with all its descendant is always stored in a continues range
9660// with the function itself going first. The root function is a script function.
9661static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
9662 ASSERT(args.length() == 2);
9663 HandleScope scope;
9664 CONVERT_CHECKED(JSValue, script, args[0]);
9665 CONVERT_ARG_CHECKED(String, source, 1);
9666 Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
9667
9668 JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
9669
9670 if (Top::has_pending_exception()) {
9671 return Failure::Exception();
9672 }
9673
9674 return result;
9675}
9676
9677// Changes the source of the script to a new_source and creates a new
9678// script representing the old version of the script source.
9679static Object* Runtime_LiveEditReplaceScript(Arguments args) {
9680 ASSERT(args.length() == 3);
9681 HandleScope scope;
9682 CONVERT_CHECKED(JSValue, original_script_value, args[0]);
9683 CONVERT_ARG_CHECKED(String, new_source, 1);
9684 CONVERT_ARG_CHECKED(String, old_script_name, 2);
9685 Handle<Script> original_script =
9686 Handle<Script>(Script::cast(original_script_value->value()));
9687
9688 Handle<String> original_source(String::cast(original_script->source()));
9689
9690 original_script->set_source(*new_source);
9691 Handle<Script> old_script = Factory::NewScript(original_source);
9692 old_script->set_name(*old_script_name);
9693 old_script->set_line_offset(original_script->line_offset());
9694 old_script->set_column_offset(original_script->column_offset());
9695 old_script->set_data(original_script->data());
9696 old_script->set_type(original_script->type());
9697 old_script->set_context_data(original_script->context_data());
9698 old_script->set_compilation_type(original_script->compilation_type());
9699 old_script->set_eval_from_shared(original_script->eval_from_shared());
9700 old_script->set_eval_from_instructions_offset(
9701 original_script->eval_from_instructions_offset());
9702
ager@chromium.org357bf652010-04-12 11:30:10 +00009703 // Drop line ends so that they will be recalculated.
9704 original_script->set_line_ends(Heap::undefined_value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009705
9706 Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
9707
9708 return *(GetScriptWrapper(old_script));
9709}
9710
9711// Replaces code of SharedFunctionInfo with a new one.
9712static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
9713 ASSERT(args.length() == 2);
9714 HandleScope scope;
9715 CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
9716 CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
9717
9718 LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
9719
9720 return Heap::undefined_value();
9721}
9722
9723// Connects SharedFunctionInfo to another script.
9724static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
9725 ASSERT(args.length() == 2);
9726 HandleScope scope;
9727 CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
9728 CONVERT_ARG_CHECKED(JSValue, script_value, 1);
9729 Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
9730
9731 LiveEdit::RelinkFunctionToScript(shared_info_array, script);
9732
9733 return Heap::undefined_value();
9734}
9735
9736// Updates positions of a shared function info (first parameter) according
9737// to script source change. Text change is described in second parameter as
9738// array of groups of 3 numbers:
9739// (change_begin, change_end, change_end_new_position).
9740// Each group describes a change in text; groups are sorted by change_begin.
ager@chromium.org357bf652010-04-12 11:30:10 +00009741// Returns an array of pairs (new source position, breakpoint_object/array)
9742// so that JS side could update positions in breakpoint objects.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009743static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
9744 ASSERT(args.length() == 2);
9745 HandleScope scope;
9746 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
9747 CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
9748
ager@chromium.org357bf652010-04-12 11:30:10 +00009749 Handle<Object> result =
9750 LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009751
ager@chromium.org357bf652010-04-12 11:30:10 +00009752 return *result;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009753}
9754
9755
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009756// For array of SharedFunctionInfo's (each wrapped in JSValue)
9757// checks that none of them have activations on stacks (of any thread).
9758// Returns array of the same length with corresponding results of
9759// LiveEdit::FunctionPatchabilityStatus type.
ager@chromium.org357bf652010-04-12 11:30:10 +00009760static Object* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
9761 ASSERT(args.length() == 2);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009762 HandleScope scope;
9763 CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00009764 CONVERT_BOOLEAN_CHECKED(do_drop, args[1]);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009765
ager@chromium.org357bf652010-04-12 11:30:10 +00009766 return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009767}
9768
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009769// Compares 2 strings line-by-line and returns diff in form of JSArray of
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00009770// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009771static Object* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
9772 ASSERT(args.length() == 2);
9773 HandleScope scope;
9774 CONVERT_ARG_CHECKED(String, s1, 0);
9775 CONVERT_ARG_CHECKED(String, s2, 1);
9776
9777 return *LiveEdit::CompareStringsLinewise(s1, s2);
9778}
9779
9780
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009781
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009782// A testing entry. Returns statement position which is the closest to
9783// source_position.
9784static Object* Runtime_GetFunctionCodePositionFromSource(Arguments args) {
9785 ASSERT(args.length() == 2);
9786 HandleScope scope;
9787 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9788 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
9789
9790 Handle<Code> code(function->code());
9791
9792 RelocIterator it(*code, 1 << RelocInfo::STATEMENT_POSITION);
9793 int closest_pc = 0;
9794 int distance = kMaxInt;
9795 while (!it.done()) {
9796 int statement_position = static_cast<int>(it.rinfo()->data());
9797 // Check if this break point is closer that what was previously found.
9798 if (source_position <= statement_position &&
9799 statement_position - source_position < distance) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00009800 closest_pc =
9801 static_cast<int>(it.rinfo()->pc() - code->instruction_start());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00009802 distance = statement_position - source_position;
9803 // Check whether we can't get any closer.
9804 if (distance == 0) break;
9805 }
9806 it.next();
9807 }
9808
9809 return Smi::FromInt(closest_pc);
9810}
9811
9812
ager@chromium.org357bf652010-04-12 11:30:10 +00009813// Calls specified function with or without entering the debugger.
9814// This is used in unit tests to run code as if debugger is entered or simply
9815// to have a stack with C++ frame in the middle.
9816static Object* Runtime_ExecuteInDebugContext(Arguments args) {
9817 ASSERT(args.length() == 2);
9818 HandleScope scope;
9819 CONVERT_ARG_CHECKED(JSFunction, function, 0);
9820 CONVERT_BOOLEAN_CHECKED(without_debugger, args[1]);
9821
9822 Handle<Object> result;
9823 bool pending_exception;
9824 {
9825 if (without_debugger) {
9826 result = Execution::Call(function, Top::global(), 0, NULL,
9827 &pending_exception);
9828 } else {
9829 EnterDebugger enter_debugger;
9830 result = Execution::Call(function, Top::global(), 0, NULL,
9831 &pending_exception);
9832 }
9833 }
9834 if (!pending_exception) {
9835 return *result;
9836 } else {
9837 return Failure::Exception();
9838 }
9839}
9840
9841
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009842#endif // ENABLE_DEBUGGER_SUPPORT
9843
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009844#ifdef ENABLE_LOGGING_AND_PROFILING
9845
9846static Object* Runtime_ProfilerResume(Arguments args) {
9847 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009848 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009849
9850 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009851 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9852 v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009853 return Heap::undefined_value();
9854}
9855
9856
9857static Object* Runtime_ProfilerPause(Arguments args) {
9858 NoHandleAllocation ha;
ager@chromium.org5c838252010-02-19 08:53:10 +00009859 ASSERT(args.length() == 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009860
9861 CONVERT_CHECKED(Smi, smi_modules, args[0]);
ager@chromium.org5c838252010-02-19 08:53:10 +00009862 CONVERT_CHECKED(Smi, smi_tag, args[1]);
9863 v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00009864 return Heap::undefined_value();
9865}
9866
9867#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00009868
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009869// Finds the script object from the script data. NOTE: This operation uses
9870// heap traversal to find the function generated for the source position
9871// for the requested break point. For lazily compiled functions several heap
9872// traversals might be required rendering this operation as a rather slow
9873// operation. However for setting break points which is normally done through
9874// some kind of user interaction the performance is not crucial.
9875static Handle<Object> Runtime_GetScriptFromScriptName(
9876 Handle<String> script_name) {
9877 // Scan the heap for Script objects to find the script with the requested
9878 // script data.
9879 Handle<Script> script;
9880 HeapIterator iterator;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009881 HeapObject* obj = NULL;
9882 while (script.is_null() && ((obj = iterator.next()) != NULL)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00009883 // If a script is found check if it has the script data requested.
9884 if (obj->IsScript()) {
9885 if (Script::cast(obj)->name()->IsString()) {
9886 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
9887 script = Handle<Script>(Script::cast(obj));
9888 }
9889 }
9890 }
9891 }
9892
9893 // If no script with the requested script data is found return undefined.
9894 if (script.is_null()) return Factory::undefined_value();
9895
9896 // Return the script found.
9897 return GetScriptWrapper(script);
9898}
9899
9900
9901// Get the script object from script data. NOTE: Regarding performance
9902// see the NOTE for GetScriptFromScriptData.
9903// args[0]: script data for the script to find the source for
9904static Object* Runtime_GetScript(Arguments args) {
9905 HandleScope scope;
9906
9907 ASSERT(args.length() == 1);
9908
9909 CONVERT_CHECKED(String, script_name, args[0]);
9910
9911 // Find the requested script.
9912 Handle<Object> result =
9913 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
9914 return *result;
9915}
9916
9917
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009918// Determines whether the given stack frame should be displayed in
9919// a stack trace. The caller is the error constructor that asked
9920// for the stack trace to be collected. The first time a construct
9921// call to this function is encountered it is skipped. The seen_caller
9922// in/out parameter is used to remember if the caller has been seen
9923// yet.
9924static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
9925 bool* seen_caller) {
9926 // Only display JS frames.
9927 if (!raw_frame->is_java_script())
9928 return false;
9929 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
9930 Object* raw_fun = frame->function();
9931 // Not sure when this can happen but skip it just in case.
9932 if (!raw_fun->IsJSFunction())
9933 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009934 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009935 *seen_caller = true;
9936 return false;
9937 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009938 // Skip all frames until we've seen the caller. Also, skip the most
9939 // obvious builtin calls. Some builtin calls (such as Number.ADD
9940 // which is invoked using 'call') are very difficult to recognize
9941 // so we're leaving them in for now.
9942 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009943}
9944
9945
9946// Collect the raw data for a stack trace. Returns an array of three
9947// element segments each containing a receiver, function and native
9948// code offset.
9949static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009950 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009951 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009952 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
9953
9954 HandleScope scope;
9955
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00009956 limit = Max(limit, 0); // Ensure that limit is not negative.
9957 int initial_size = Min(limit, 10);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009958 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009959
9960 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009961 // If the caller parameter is a function we skip frames until we're
9962 // under it before starting to collect.
9963 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009964 int cursor = 0;
9965 int frames_seen = 0;
9966 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009967 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009968 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009969 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009970 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009971 Object* recv = frame->receiver();
9972 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009973 Address pc = frame->pc();
9974 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00009975 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009976 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009977 if (cursor + 2 < elements->length()) {
9978 elements->set(cursor++, recv);
9979 elements->set(cursor++, fun);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009980 elements->set(cursor++, offset);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009981 } else {
9982 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00009983 Handle<Object> recv_handle(recv);
9984 Handle<Object> fun_handle(fun);
9985 SetElement(result, cursor++, recv_handle);
9986 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009987 SetElement(result, cursor++, Handle<Smi>(offset));
9988 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009989 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009990 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009991 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00009992
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009993 result->set_length(Smi::FromInt(cursor));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009994 return *result;
9995}
9996
9997
ager@chromium.org3811b432009-10-28 14:53:37 +00009998// Returns V8 version as a string.
9999static Object* Runtime_GetV8Version(Arguments args) {
10000 ASSERT_EQ(args.length(), 0);
10001
10002 NoHandleAllocation ha;
10003
10004 const char* version_string = v8::V8::GetVersion();
10005
10006 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
10007}
10008
10009
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010010static Object* Runtime_Abort(Arguments args) {
10011 ASSERT(args.length() == 2);
10012 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
10013 Smi::cast(args[1])->value());
10014 Top::PrintStack();
10015 OS::Abort();
10016 UNREACHABLE();
10017 return NULL;
10018}
10019
10020
ager@chromium.orgc4c92722009-11-18 14:12:51 +000010021static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
10022 ASSERT(args.length() == 0);
10023 HandleScope::DeleteExtensions();
10024 return Heap::undefined_value();
10025}
10026
10027
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010028static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
10029 ASSERT(index % 2 == 0); // index of the key
10030 ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
10031 ASSERT(index < cache_obj->length());
10032
10033 HandleScope scope;
10034
10035 Handle<FixedArray> cache(cache_obj);
10036 Handle<Object> key(key_obj);
10037 Handle<JSFunction> factory(JSFunction::cast(
10038 cache->get(JSFunctionResultCache::kFactoryIndex)));
10039 // TODO(antonm): consider passing a receiver when constructing a cache.
10040 Handle<Object> receiver(Top::global_context()->global());
10041
10042 Handle<Object> value;
10043 {
10044 // This handle is nor shared, nor used later, so it's safe.
10045 Object** argv[] = { key.location() };
10046 bool pending_exception = false;
10047 value = Execution::Call(factory,
10048 receiver,
10049 1,
10050 argv,
10051 &pending_exception);
10052 if (pending_exception) return Failure::Exception();
10053 }
10054
10055 cache->set(index, *key);
10056 cache->set(index + 1, *value);
10057 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
10058
10059 return *value;
10060}
10061
10062
10063static Object* Runtime_GetFromCache(Arguments args) {
10064 // This is only called from codegen, so checks might be more lax.
10065 CONVERT_CHECKED(FixedArray, cache, args[0]);
10066 Object* key = args[1];
10067
10068 const int finger_index =
10069 Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
10070
10071 Object* o = cache->get(finger_index);
10072 if (o == key) {
10073 // The fastest case: hit the same place again.
10074 return cache->get(finger_index + 1);
10075 }
10076
10077 for (int i = finger_index - 2;
10078 i >= JSFunctionResultCache::kEntriesIndex;
10079 i -= 2) {
10080 o = cache->get(i);
10081 if (o == key) {
10082 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10083 return cache->get(i + 1);
10084 }
10085 }
10086
10087 const int size =
10088 Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
10089 ASSERT(size <= cache->length());
10090
10091 for (int i = size - 2; i > finger_index; i -= 2) {
10092 o = cache->get(i);
10093 if (o == key) {
10094 cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
10095 return cache->get(i + 1);
10096 }
10097 }
10098
10099 // Cache miss. If we have spare room, put new data into it, otherwise
10100 // evict post finger entry which must be least recently used.
10101 if (size < cache->length()) {
10102 cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
10103 return CacheMiss(cache, size, key);
10104 } else {
antonm@chromium.org397e23c2010-04-21 12:00:05 +000010105 int target_index = finger_index + JSFunctionResultCache::kEntrySize;
10106 if (target_index == cache->length()) {
10107 target_index = JSFunctionResultCache::kEntriesIndex;
10108 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000010109 return CacheMiss(cache, target_index, key);
10110 }
10111}
10112
kasper.lund44510672008-07-25 07:37:58 +000010113#ifdef DEBUG
10114// ListNatives is ONLY used by the fuzz-natives.js in debug mode
10115// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010116static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +000010117 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010118 HandleScope scope;
10119 Handle<JSArray> result = Factory::NewJSArray(0);
10120 int index = 0;
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010121 bool inline_runtime_functions = false;
ager@chromium.orga1645e22009-09-09 19:27:10 +000010122#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010123 { \
10124 HandleScope inner; \
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010125 Handle<String> name; \
10126 /* Inline runtime functions have an underscore in front of the name. */ \
10127 if (inline_runtime_functions) { \
10128 name = Factory::NewStringFromAscii( \
10129 Vector<const char>("_" #Name, StrLength("_" #Name))); \
10130 } else { \
10131 name = Factory::NewStringFromAscii( \
10132 Vector<const char>(#Name, StrLength(#Name))); \
10133 } \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010134 Handle<JSArray> pair = Factory::NewJSArray(0); \
10135 SetElement(pair, 0, name); \
10136 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
10137 SetElement(result, index++, pair); \
10138 }
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010139 inline_runtime_functions = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010140 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
vegorov@chromium.orgf8372902010-03-15 10:26:20 +000010141 inline_runtime_functions = true;
10142 INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010143#undef ADD_ENTRY
10144 return *result;
10145}
kasper.lund44510672008-07-25 07:37:58 +000010146#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010147
10148
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010149static Object* Runtime_Log(Arguments args) {
10150 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +000010151 CONVERT_CHECKED(String, format, args[0]);
10152 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010153 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +000010154 Logger::LogRuntime(chars, elms);
10155 return Heap::undefined_value();
10156}
10157
10158
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010159static Object* Runtime_IS_VAR(Arguments args) {
10160 UNREACHABLE(); // implemented as macro in the parser
10161 return NULL;
10162}
10163
10164
10165// ----------------------------------------------------------------------------
10166// Implementation of Runtime
10167
ager@chromium.orga1645e22009-09-09 19:27:10 +000010168#define F(name, nargs, ressize) \
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010169 { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +000010170 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010171
10172static Runtime::Function Runtime_functions[] = {
10173 RUNTIME_FUNCTION_LIST(F)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010174 { NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010175};
10176
10177#undef F
10178
10179
10180Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
10181 ASSERT(0 <= fid && fid < kNofFunctions);
10182 return &Runtime_functions[fid];
10183}
10184
10185
10186Runtime::Function* Runtime::FunctionForName(const char* name) {
10187 for (Function* f = Runtime_functions; f->name != NULL; f++) {
10188 if (strcmp(f->name, name) == 0) {
10189 return f;
10190 }
10191 }
10192 return NULL;
10193}
10194
10195
10196void Runtime::PerformGC(Object* result) {
10197 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010198 if (failure->IsRetryAfterGC()) {
10199 // Try to do a garbage collection; ignore it if it fails. The C
10200 // entry stub will throw an out-of-memory exception in that case.
10201 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
10202 } else {
10203 // Handle last resort GC and make sure to allow future allocations
10204 // to grow the heap without causing GCs (if possible).
10205 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +000010206 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +000010207 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010208}
10209
10210
10211} } // namespace v8::internal